#At file:///opt/local/work/next-4284-stage/ based on revid:kostja@stripped
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
=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc 2010-01-20 11:19:31 +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,
@@ -83,10 +106,6 @@ public:
inline static MDL_lock *create(const MDL_key *key);
- 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),
cached_object(NULL),
@@ -102,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.
@@ -127,10 +147,6 @@ private:
uint m_ref_usage;
uint m_ref_release;
bool m_is_destroyed;
-
-private:
- inline static void destroy(MDL_lock *lock);
- 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,10 +240,183 @@ 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);
}
@@ -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,169 +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= 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;
+ 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);
-}
/**
@@ -991,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;
@@ -1030,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();
@@ -1161,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;
@@ -1340,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;
@@ -1370,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
{
/*
@@ -1424,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
{
/*
@@ -1896,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))
@@ -1934,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);
@@ -1975,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();
Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20100120123049-p2uo244b4rfpn2i8.bundle
| Thread |
|---|
| • bzr commit into mysql-5.6-next-mr branch (kostja:3060) Bug#38924Bug#46272 | Konstantin Osipov | 20 Jan |