3060 Konstantin Osipov 2010-01-20
Bug#46272/Bug#38924 review fixes in progress.
Introduce class MDL_map to hold all MDL locks
taken in the server.
modified:
sql/mdl.cc
3059 Konstantin Osipov 2010-01-20
Bug#46272/Bug#38924 review fixes in progress.
Misc fixes.
modified:
sql/mdl.cc
sql/mdl.h
3058 Konstantin Osipov 2010-01-20
Bug#46272/Bug#38924 review fixes in progress.
Update MDL_key class to be more compact.
Simplify the comparison method, used when sorting MDL_keys.
modified:
sql/mdl.h
=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc 2010-01-20 10:17:29 +0000
+++ b/sql/mdl.cc 2010-01-20 12:30:49 +0000
@@ -21,6 +21,29 @@
static bool mdl_initialized= 0;
+
+/**
+ A collection of all MDL locks. A singleton,
+ there is only one instance of the map in the server.
+ Maps MDL_key to MDL_lock instances.
+*/
+
+class MDL_map
+{
+public:
+ pthread_mutex_t m_mutex;
+ HASH m_locks;
+
+ void init();
+ void destroy();
+ MDL_lock *find(const MDL_key *key);
+ MDL_lock *find_or_insert(const MDL_key *key);
+ void remove(MDL_lock *lock);
+private:
+ bool move_from_hash_to_lock_mutex(MDL_lock *lock);
+};
+
+
/**
The lock context. Created internally for an acquired lock.
For a given name, there exists only one MDL_lock instance,
@@ -78,15 +101,10 @@ public:
return has_locks;
}
virtual bool can_grant_lock(const MDL_context *requestor_ctx,
- enum_mdl_type type, bool is_upgrade) = 0;
- virtual void wake_up_waiters() = 0;
+ enum_mdl_type type, bool is_upgrade)= 0;
+ virtual void wake_up_waiters()= 0;
inline static MDL_lock *create(const MDL_key *key);
- inline static void destroy(MDL_lock *lock);
-
- static MDL_lock *find(const MDL_key *key);
- static MDL_lock *find_or_insert(const MDL_key *key);
- static void destroy_or_delegate_destruction(MDL_lock *lock);
MDL_lock(const MDL_key *key_arg)
: key(key_arg),
@@ -103,20 +121,21 @@ public:
{
pthread_mutex_destroy(&m_mutex);
}
-private:
+ inline static void destroy(MDL_lock *lock);
+public:
/**
These three members are used to make it possible to separate
- the LOCK_mdl_hash mutex and MDL_lock::m_mutex in
- MDL_lock::find_or_insert() for increased scalability.
+ the mdl_locks.m_mutex mutex and MDL_lock::m_mutex in
+ MDL_map::find_or_insert() for increased scalability.
The 'm_is_destroyed' member is only set by destroyers that
- have both the LOCK_mdl_hash and MDL_lock::m_mutex, thus
+ have both the mdl_locks.m_mutex and MDL_lock::m_mutex, thus
holding any of the mutexes is sufficient to read it.
The 'm_ref_usage; is incremented under protection by
- LOCK_mdl_hash, but when 'm_is_destroyed' is set to TRUE, this
+ mdl_locks.m_mutex, but when 'm_is_destroyed' is set to TRUE, this
member is moved to be protected by the MDL_lock::m_mutex.
- This means that the MDL_lock::find_or_insert() which only
+ This means that the MDL_map::find_or_insert() which only
holds the MDL_lock::m_mutex can compare it to 'm_ref_release'
- without acquiring LOCK_mdl_hash again and if equal it can also
+ without acquiring mdl_locks.m_mutex again and if equal it can also
destroy the lock object safely.
The 'm_ref_release' is incremented under protection by
MDL_lock::m_mutex.
@@ -128,9 +147,6 @@ private:
uint m_ref_usage;
uint m_ref_release;
bool m_is_destroyed;
-
-private:
- static bool move_from_hash_to_lock_mutex(MDL_lock *lock);
};
@@ -170,9 +186,7 @@ public:
};
-static pthread_mutex_t LOCK_mdl_hash;
-static HASH mdl_locks;
-
+static MDL_map mdl_locks;
extern "C"
{
@@ -210,9 +224,7 @@ void mdl_init()
{
DBUG_ASSERT(! mdl_initialized);
mdl_initialized= TRUE;
- pthread_mutex_init(&LOCK_mdl_hash, NULL);
- my_hash_init(&mdl_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
- mdl_locks_key, 0, 0);
+ mdl_locks.init();
}
@@ -228,13 +240,186 @@ void mdl_destroy()
if (mdl_initialized)
{
mdl_initialized= FALSE;
- DBUG_ASSERT(!mdl_locks.records);
- pthread_mutex_destroy(&LOCK_mdl_hash);
- my_hash_free(&mdl_locks);
+ mdl_locks.destroy();
}
}
+/** Initialize the global hash containing all MDL locks. */
+
+void MDL_map::init()
+{
+ pthread_mutex_init(&m_mutex, NULL);
+ my_hash_init(&m_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
+ mdl_locks_key, 0, 0);
+}
+
+
+/**
+ Destroy the global hash containing all MDL locks.
+ @pre It must be empty.
+*/
+
+void MDL_map::destroy()
+{
+ DBUG_ASSERT(!m_locks.records);
+ pthread_mutex_destroy(&m_mutex);
+ my_hash_free(&m_locks);
+}
+
+
+/**
+ Find MDL_lock object corresponding to the key, create it
+ if it does not exist.
+
+ @retval non-NULL - Success. MDL_lock instance for the key with
+ locked MDL_lock::m_mutex.
+ @retval NULL - Failure (OOM).
+*/
+
+MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key)
+{
+ MDL_lock *lock;
+
+retry:
+ pthread_mutex_lock(&m_mutex);
+ if (!(lock= (MDL_lock*) my_hash_search(&m_locks,
+ mdl_key->ptr(),
+ mdl_key->length())))
+ {
+ lock= MDL_lock::create(mdl_key);
+ if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
+ {
+ pthread_mutex_unlock(&m_mutex);
+ MDL_lock::destroy(lock);
+ return NULL;
+ }
+ }
+
+ if (move_from_hash_to_lock_mutex(lock))
+ goto retry;
+
+ return lock;
+}
+
+
+/**
+ Find MDL_lock object corresponding to the key.
+
+ @retval non-NULL - MDL_lock instance for the key with locked
+ MDL_lock::m_mutex.
+ @retval NULL - There was no MDL_lock for the key.
+*/
+
+MDL_lock* MDL_map::find(const MDL_key *mdl_key)
+{
+ MDL_lock *lock;
+
+retry:
+ pthread_mutex_lock(&m_mutex);
+ if (!(lock= (MDL_lock*) my_hash_search(&m_locks,
+ mdl_key->ptr(),
+ mdl_key->length())))
+ {
+ pthread_mutex_unlock(&m_mutex);
+ return NULL;
+ }
+
+ if (move_from_hash_to_lock_mutex(lock))
+ goto retry;
+
+ return lock;
+}
+
+
+/**
+ Release mdl_locks.m_mutex mutex and lock MDL_lock::m_mutex for lock
+ object from the hash. Handle situation when object was released
+ while the held no mutex.
+
+ @retval FALSE - Success.
+ @retval TRUE - Object was released while we held no mutex, caller
+ should re-try looking up MDL_lock object in the hash.
+*/
+
+bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
+{
+ DBUG_ASSERT(! lock->m_is_destroyed);
+ safe_mutex_assert_owner(&m_mutex);
+
+ /*
+ We increment m_ref_usage which is a reference counter protected by
+ mdl_locks.m_mutex under the condition it is present in the hash and
+ m_is_destroyed is FALSE.
+ */
+ lock->m_ref_usage++;
+ pthread_mutex_unlock(&m_mutex);
+
+ pthread_mutex_lock(&lock->m_mutex);
+ lock->m_ref_release++;
+ if (unlikely(lock->m_is_destroyed))
+ {
+ /*
+ Object was released while we held no mutex, we need to release
+ it if no others hold references to it, our reference count
+ ensured that the object as such haven't got its memory released
+ yet. We can also safely check m_ref_usage and m_ref_release to each
+ other since the object is removed from the hash so no one will
+ be able to find it and increment m_ref_usage anymore.
+ */
+ uint ref_usage= lock->m_ref_usage;
+ uint ref_release= lock->m_ref_release;
+ pthread_mutex_unlock(&lock->m_mutex);
+ if (ref_usage == ref_release)
+ MDL_lock::destroy(lock);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ Destroy MDL_lock object or delegate this resposibility to whatever thread
+ which will hold last outstanding reference to it,
+*/
+
+void MDL_map::remove(MDL_lock *lock)
+{
+ uint ref_usage, ref_release;
+
+ safe_mutex_assert_owner(&lock->m_mutex);
+
+ if (lock->cached_object)
+ (*lock->cached_object_release_hook)(lock->cached_object);
+
+ /*
+ Destroy the MDL_lock object, but ensure that anyone that is
+ holding a reference to the object is not remaining, if so he
+ has the responsibility to release it.
+
+ Setting of m_is_destroyed to TRUE while holding _both_
+ mdl_locks.m_mutex and MDL_lock::m_mutex mutexes transfers the
+ protection of m_ref_usage from mdl_locks.m_mutex to
+ MDL_lock::m_mutex while removal of object from the hash makes
+ it read-only. Therefore whoever acquires MDL_lock::m_mutex next
+ will see most up to date version of m_ref_usage.
+
+ This means that when m_is_destroyed is TRUE and we hold the
+ MDL_lock::m_mutex we can safely read the m_ref_usage
+ member.
+ */
+ pthread_mutex_lock(&m_mutex);
+ my_hash_delete(&m_locks, (uchar*) lock);
+ lock->m_is_destroyed= TRUE;
+ ref_usage= lock->m_ref_usage;
+ ref_release= lock->m_ref_release;
+ pthread_mutex_unlock(&lock->m_mutex);
+ if (ref_usage == ref_release)
+ MDL_lock::destroy(lock);
+ pthread_mutex_unlock(&m_mutex);
+}
+
+
/**
Initialize a metadata locking context.
@@ -242,16 +427,9 @@ void mdl_destroy()
*/
MDL_context::MDL_context()
+ :m_lt_or_ha_sentinel(NULL),
+ m_thd(NULL)
{
- m_lt_or_ha_sentinel= NULL;
- /*
- FIXME: In reset_n_backup_open_tables_state,
- we abuse "init" as a reset, i.e. call it on an already
- constructed non-empty object. This is why we can't
- rely here on the default constructors of I_P_List
- to empty the list.
- */
- m_tickets.empty();
pthread_cond_init(&m_ctx_wakeup_cond, NULL);
}
@@ -369,170 +547,22 @@ MDL_request::create(MDL_key::enum_mdl_na
inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
{
- if (mdl_key->mdl_namespace() == MDL_key::GLOBAL)
- return new MDL_global_lock(mdl_key);
- else
- return new MDL_object_lock(mdl_key);
-}
-
-
-void MDL_lock::destroy(MDL_lock *lock)
-{
- delete lock;
-}
-
-
-/**
- Release LOCK_mdl_hash mutex and lock MDL_lock::m_mutex for lock
- object from the hash. Handle situation when object was released
- while the held no mutex.
-
- @retval FALSE - Success.
- @retval TRUE - Object was released while we held no mutex, caller
- should re-try looking up MDL_lock object in the hash.
-*/
-
-bool MDL_lock::move_from_hash_to_lock_mutex(MDL_lock *lock)
-{
- DBUG_ASSERT(! lock->m_is_destroyed);
- safe_mutex_assert_owner(&LOCK_mdl_hash);
-
- /*
- We increment m_ref_usage which is a reference counter protected by
- LOCK_mdl_hash under the condition it is present in the hash and
- m_is_destroyed is FALSE.
- */
- lock->m_ref_usage++;
- pthread_mutex_unlock(&LOCK_mdl_hash);
-
- pthread_mutex_lock(&lock->m_mutex);
- lock->m_ref_release++;
- if (unlikely(lock->m_is_destroyed))
+ switch (mdl_key->mdl_namespace())
{
- /*
- Object was released while we held no mutex, we need to release
- it if no others hold references to it, our reference count
- ensured that the object as such haven't got its memory released
- yet. We can also safely check m_ref_usage and m_ref_release to each
- other since the object is removed from the hash so no one will
- be able to find it and increment m_ref_usage anymore.
- */
- uint ref_usage, ref_release;
- ref_usage= lock->m_ref_usage;
- ref_release= lock->m_ref_release;
- pthread_mutex_unlock(&lock->m_mutex);
- if (ref_usage == ref_release)
- MDL_lock::destroy(lock);
- return TRUE;
+ case MDL_key::GLOBAL:
+ return new MDL_global_lock(mdl_key);
+ default:
+ return new MDL_object_lock(mdl_key);
}
- return FALSE;
}
-/**
- Find MDL_lock object corresponding to the key.
-
- @retval non-NULL - MDL_lock instance for the key with locked
- MDL_lock::m_mutex.
- @retval NULL - There was no MDL_lock for the key.
-*/
-
-MDL_lock* MDL_lock::find(const MDL_key *mdl_key)
-{
- MDL_lock *lock;
-
-retry:
- pthread_mutex_lock(&LOCK_mdl_hash);
- if (!(lock= (MDL_lock*) my_hash_search(&mdl_locks,
- mdl_key->ptr(),
- mdl_key->length())))
- {
- pthread_mutex_unlock(&LOCK_mdl_hash);
- return NULL;
- }
-
- if (move_from_hash_to_lock_mutex(lock))
- goto retry;
-
- return lock;
-}
-
-
-/**
- Find MDL_lock object corresponding to the key, create it
- if it does not exist.
-
- @retval non-NULL - Success. MDL_lock instance for the key with
- locked MDL_lock::m_mutex.
- @retval NULL - Failure (OOM).
-*/
-
-MDL_lock* MDL_lock::find_or_insert(const MDL_key *mdl_key)
+void MDL_lock::destroy(MDL_lock *lock)
{
- MDL_lock *lock;
-
-retry:
- pthread_mutex_lock(&LOCK_mdl_hash);
- if (!(lock= (MDL_lock*) my_hash_search(&mdl_locks,
- mdl_key->ptr(),
- mdl_key->length())))
- {
- lock= MDL_lock::create(mdl_key);
- if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock))
- {
- pthread_mutex_unlock(&LOCK_mdl_hash);
- MDL_lock::destroy(lock);
- return NULL;
- }
- }
-
- if (move_from_hash_to_lock_mutex(lock))
- goto retry;
-
- return lock;
+ delete lock;
}
-/**
- Destroy MDL_lock object or delegate this resposibility to whatever thread
- which will hold last outstanding reference to it,
-*/
-
-void MDL_lock::destroy_or_delegate_destruction(MDL_lock *lock)
-{
- uint ref_usage, ref_release;
-
- safe_mutex_assert_owner(&lock->m_mutex);
-
- if (lock->cached_object)
- (*lock->cached_object_release_hook)(lock->cached_object);
-
- /*
- Destroy the MDL_lock object, but ensure that anyone that is
- holding a reference to the object is not remaining, if so he
- has the responsibility to release it.
-
- Setting of m_is_destroyed to TRUE while holding _both_
- LOCK_mdl_hash and MDL_lock::m_mutex mutexes transfers the
- protection of m_ref_usage from LOCK_mdl_hash to
- MDL_lock::m_mutex while removal of object from the hash makes
- it read-only. Therefore whoever acquires MDL_lock::m_mutex next
- will see most up to date version of m_ref_usage.
-
- This means that when m_is_destroyed is TRUE and we hold the
- MDL_lock::m_mutex we can safely read the m_ref_usage
- member.
- */
- pthread_mutex_lock(&LOCK_mdl_hash);
- my_hash_delete(&mdl_locks, (uchar *)lock);
- lock->m_is_destroyed= TRUE;
- ref_usage= lock->m_ref_usage;
- ref_release= lock->m_ref_release;
- pthread_mutex_unlock(&lock->m_mutex);
- if (ref_usage == ref_release)
- MDL_lock::destroy(lock);
- pthread_mutex_unlock(&LOCK_mdl_hash);
-}
/**
@@ -693,29 +723,32 @@ MDL_global_lock::can_grant_lock(const MD
/**
- Wake up contexts which are waiting to acquire global metadata and
- which may succeed now, when we released global lock or removed request
- for global shared lock from waiters list (the latter can happen when
- context trying to acquire global shared lock is killed).
+ Wake up contexts which are waiting to acquire the global
+ metadata lock and which may succeed now, when we released it, or
+ removed a blocking request for it from the waiters list.
+ The latter can happen when the context trying to acquire the
+ global shared lock is killed.
*/
void MDL_global_lock::wake_up_waiters()
{
/*
- There are no active locks or they are of INTENION EXCLUSIVE type.
- Wake up contexts waiting for INTENTION EXCLUSIVE lock if there
- are no pending requests for global SHARED lock.
- (Happens when we release global SHARED lock or abort wait for
- global SHARED lock).
+ If there are no active locks or they are of INTENTION
+ EXCLUSIVE type and there are no pending requests for global
+ SHARED lock, wake up contexts waiting for an INTENTION
+ EXCLUSIVE lock.
+ This happens when we release the global SHARED lock or abort
+ or remove a pending request for it, i.e. abort the
+ context waiting for it.
*/
if ((granted.is_empty() ||
granted.front()->m_type == MDL_INTENTION_EXCLUSIVE) &&
waiting_shared.is_empty() && ! waiting_exclusive.is_empty())
{
MDL_lock::Ticket_iterator it(waiting_exclusive);
- MDL_ticket *wake_up_ticket;
- while ((wake_up_ticket= it++))
- wake_up_ticket->get_ctx()->wake_up();
+ MDL_ticket *awake_ticket;
+ while ((awake_ticket= it++))
+ awake_ticket->get_ctx()->awake();
}
/*
@@ -732,9 +765,9 @@ void MDL_global_lock::wake_up_waiters()
! waiting_shared.is_empty())
{
MDL_lock::Ticket_iterator it(waiting_shared);
- MDL_ticket *wake_up_ticket;
- while ((wake_up_ticket= it++))
- wake_up_ticket->get_ctx()->wake_up();
+ MDL_ticket *awake_ticket;
+ while ((awake_ticket= it++))
+ awake_ticket->get_ctx()->awake();
}
}
@@ -864,7 +897,7 @@ void MDL_object_lock::wake_up_waiters()
MDL_lock::Ticket_iterator it(waiting_shared);
MDL_ticket *waiting_ticket;
while ((waiting_ticket= it++))
- waiting_ticket->get_ctx()->wake_up();
+ waiting_ticket->get_ctx()->awake();
}
/*
@@ -876,7 +909,7 @@ void MDL_object_lock::wake_up_waiters()
MDL_lock::Ticket_iterator it(waiting_exclusive);
MDL_ticket *waiting_ticket;
while ((waiting_ticket= it++))
- waiting_ticket->get_ctx()->wake_up();
+ waiting_ticket->get_ctx()->awake();
}
}
@@ -989,7 +1022,7 @@ MDL_context::acquire_lock_impl(MDL_reque
return TRUE;
/* The below call also implicitly locks MDL_lock::m_mutex. */
- if (! (lock= MDL_lock::find_or_insert(key)))
+ if (! (lock= mdl_locks.find_or_insert(key)))
{
MDL_ticket::destroy(ticket);
return TRUE;
@@ -1028,7 +1061,7 @@ MDL_context::acquire_lock_impl(MDL_reque
else
lock->waiting_exclusive.remove(ticket);
if (lock->is_empty())
- MDL_lock::destroy_or_delegate_destruction(lock);
+ mdl_locks.remove(lock);
else
{
lock->wake_up_waiters();
@@ -1159,7 +1192,7 @@ MDL_context::try_acquire_lock_impl(MDL_r
return TRUE;
/* The below call also implicitly locks MDL_lock::m_mutex. */
- if (!(lock= MDL_lock::find_or_insert(key)))
+ if (!(lock= mdl_locks.find_or_insert(key)))
{
MDL_ticket::destroy(ticket);
return TRUE;
@@ -1276,9 +1309,9 @@ void notify_shared_lock(THD *thd, MDL_ti
/*
If the thread that holds the conflicting lock is waiting in MDL
- subsystem it has to be woken up by calling MDL_context::wake_up().
+ subsystem it has to be woken up by calling MDL_context::awake().
*/
- conflicting_ticket->get_ctx()->wake_up();
+ conflicting_ticket->get_ctx()->awake();
/*
If it is waiting on table-level lock or some other non-MDL resource
we delegate its waking up to code outside of MDL.
@@ -1338,7 +1371,7 @@ bool MDL_context::acquire_exclusive_lock
return TRUE;
/* The below call also implicitly locks MDL_lock::m_mutex. */
- if (!(lock= MDL_lock::find_or_insert(key)))
+ if (!(lock= mdl_locks.find_or_insert(key)))
{
MDL_ticket::destroy(ticket);
return TRUE;
@@ -1368,7 +1401,7 @@ bool MDL_context::acquire_exclusive_lock
/* Get rid of pending ticket. */
lock->waiting_exclusive.remove(ticket);
if (lock->is_empty())
- MDL_lock::destroy_or_delegate_destruction(lock);
+ mdl_locks.remove(lock);
else
{
/*
@@ -1422,7 +1455,7 @@ bool MDL_context::acquire_exclusive_lock
/* Get rid of pending ticket. */
lock->waiting_exclusive.remove(ticket);
if (lock->is_empty())
- MDL_lock::destroy_or_delegate_destruction(lock);
+ mdl_locks.remove(lock);
else
{
/*
@@ -1894,7 +1927,7 @@ MDL_context::wait_for_locks(MDL_request_
mdl_request->type == MDL_INTENTION_EXCLUSIVE)
{
/* The below call also implicitly locks MDL_lock::m_mutex. */
- if (! (lock= MDL_lock::find(key)))
+ if (! (lock= mdl_locks.find(key)))
continue;
if (lock->can_grant_lock(this, mdl_request->type, FALSE))
@@ -1932,7 +1965,7 @@ MDL_context::wait_for_locks(MDL_request_
else
lock->waiting_exclusive.remove(pending_ticket);
if (lock->is_empty())
- MDL_lock::destroy_or_delegate_destruction(lock);
+ mdl_locks.remove(lock);
else
pthread_mutex_unlock(&lock->m_mutex);
MDL_ticket::destroy(pending_ticket);
@@ -1973,7 +2006,7 @@ void MDL_context::release_lock(MDL_ticke
lock->granted.remove(ticket);
if (lock->is_empty())
- MDL_lock::destroy_or_delegate_destruction(lock);
+ mdl_locks.remove(lock);
else
{
lock->wake_up_waiters();
@@ -2078,7 +2111,7 @@ void MDL_ticket::downgrade_exclusive_loc
MDL_lock::Ticket_iterator it(m_lock->waiting_shared);
MDL_ticket *ticket;
while ((ticket= it++))
- ticket->get_ctx()->wake_up();
+ ticket->get_ctx()->awake();
}
pthread_mutex_unlock(&m_lock->m_mutex);
=== modified file 'sql/mdl.h'
--- a/sql/mdl.h 2010-01-20 10:40:14 +0000
+++ b/sql/mdl.h 2010-01-20 11:19:31 +0000
@@ -316,7 +316,6 @@ private:
m_lock(NULL)
{}
-
static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg);
static void destroy(MDL_ticket *ticket);
private:
@@ -420,7 +419,7 @@ public:
/**
Wake up context which is waiting for a change of MDL_lock state.
*/
- void wake_up()
+ void awake()
{
pthread_cond_signal(&m_ctx_wakeup_cond);
}
Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20100120123049-p2uo244b4rfpn2i8.bundle
| Thread |
|---|
| • bzr push into mysql-5.6-next-mr branch (kostja:3058 to 3060) Bug#38924Bug#46272 | Konstantin Osipov | 20 Jan |