summaryrefslogtreecommitdiff
path: root/sql/mdl.h
diff options
context:
space:
mode:
Diffstat (limited to 'sql/mdl.h')
-rw-r--r--sql/mdl.h237
1 files changed, 181 insertions, 56 deletions
diff --git a/sql/mdl.h b/sql/mdl.h
index 68f24a7a0e8..13de60284da 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -1,6 +1,6 @@
#ifndef MDL_H
#define MDL_H
-/* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,8 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
#if defined(__IBMC__) || defined(__IBMCPP__)
/* Further down, "next_in_lock" and "next_in_context" have the same type,
@@ -26,15 +26,97 @@
#include "sql_plist.h"
#include <my_sys.h>
-#include <my_pthread.h>
#include <m_string.h>
#include <mysql_com.h>
+#include <hash.h>
+
+#include <algorithm>
class THD;
class MDL_context;
class MDL_lock;
class MDL_ticket;
+bool ok_for_lower_case_names(const char *name);
+
+/**
+ @def ENTER_COND(C, M, S, O)
+ Start a wait on a condition.
+ @param C the condition to wait on
+ @param M the associated mutex
+ @param S the new stage to enter
+ @param O the previous stage
+ @sa EXIT_COND().
+*/
+#define ENTER_COND(C, M, S, O) enter_cond(C, M, S, O, __func__, __FILE__, __LINE__)
+
+/**
+ @def EXIT_COND(S)
+ End a wait on a condition
+ @param S the new stage to enter
+*/
+#define EXIT_COND(S) exit_cond(S, __func__, __FILE__, __LINE__)
+
+/**
+ An interface to separate the MDL module from the THD, and the rest of the
+ server code.
+ */
+
+class MDL_context_owner
+{
+public:
+ virtual ~MDL_context_owner() {}
+
+ /**
+ Enter a condition wait.
+ For @c enter_cond() / @c exit_cond() to work the mutex must be held before
+ @c enter_cond(); this mutex is then released by @c exit_cond().
+ Usage must be: lock mutex; enter_cond(); your code; exit_cond().
+ @param cond the condition to wait on
+ @param mutex the associated mutex
+ @param [in] stage the stage to enter, or NULL
+ @param [out] old_stage the previous stage, or NULL
+ @param src_function function name of the caller
+ @param src_file file name of the caller
+ @param src_line line number of the caller
+ @sa ENTER_COND(), THD::enter_cond()
+ @sa EXIT_COND(), THD::exit_cond()
+ */
+ virtual void enter_cond(mysql_cond_t *cond, mysql_mutex_t *mutex,
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line) = 0;
+
+ /**
+ @def EXIT_COND(S)
+ End a wait on a condition
+ @param [in] stage the new stage to enter
+ @param src_function function name of the caller
+ @param src_file file name of the caller
+ @param src_line line number of the caller
+ @sa ENTER_COND(), THD::enter_cond()
+ @sa EXIT_COND(), THD::exit_cond()
+ */
+ virtual void exit_cond(const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line) = 0;
+ /**
+ Has the owner thread been killed?
+ */
+ virtual int is_killed() = 0;
+
+ /**
+ This one is only used for DEBUG_SYNC.
+ (Do not use it to peek/poke into other parts of THD.)
+ */
+ virtual THD* get_thd() = 0;
+
+ /**
+ @see THD::notify_shared_lock()
+ */
+ virtual bool notify_shared_lock(MDL_context_owner *in_use,
+ bool needs_thr_lock_abort) = 0;
+};
/**
Type of metadata lock request.
@@ -114,6 +196,15 @@ enum enum_mdl_type {
*/
MDL_SHARED_WRITE,
/*
+ An upgradable shared metadata lock for cases when there is an intention
+ to modify (and not just read) data in the table.
+ Can be upgraded to MDL_SHARED_NO_WRITE and MDL_EXCLUSIVE.
+ A connection holding SU lock can read table metadata and modify or read
+ table data (after acquiring appropriate table and row-level locks).
+ To be used for the first phase of ALTER TABLE.
+ */
+ MDL_SHARED_UPGRADABLE,
+ /*
An upgradable shared metadata lock which blocks all attempts to update
table data, allowing reads.
A connection holding this kind of lock can read table metadata and read
@@ -189,6 +280,10 @@ enum enum_mdl_duration {
class MDL_key
{
public:
+#ifdef HAVE_PSI_INTERFACE
+ static void init_psi_keys();
+#endif
+
/**
Object namespaces.
Sic: when adding a new member to this enum make sure to
@@ -212,6 +307,7 @@ public:
TRIGGER,
EVENT,
COMMIT,
+ USER_LOCK, /* user level locks. */
/* This should be the last ! */
NAMESPACE_END };
@@ -247,15 +343,22 @@ public:
are not longer than NAME_LEN. Still we play safe and try to avoid
buffer overruns.
*/
- m_db_name_length= (uint16) (strmake(m_ptr + 1, db, NAME_LEN) - m_ptr - 1);
- m_length= (uint16) (strmake(m_ptr + m_db_name_length + 2, name, NAME_LEN) -
- m_ptr + 1);
+ DBUG_ASSERT(strlen(db) <= NAME_LEN);
+ DBUG_ASSERT(strlen(name) <= NAME_LEN);
+ m_db_name_length= static_cast<uint16>(strmake(m_ptr + 1, db, NAME_LEN) -
+ m_ptr - 1);
+ m_length= static_cast<uint16>(strmake(m_ptr + m_db_name_length + 2, name,
+ NAME_LEN) - m_ptr + 1);
+ m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1,
+ m_length - 1);
+ DBUG_ASSERT(mdl_namespace == USER_LOCK || ok_for_lower_case_names(db));
}
void mdl_key_init(const MDL_key *rhs)
{
memcpy(m_ptr, rhs->m_ptr, rhs->m_length);
m_length= rhs->m_length;
m_db_name_length= rhs->m_db_name_length;
+ m_hash_value= rhs->m_hash_value;
}
bool is_equal(const MDL_key *rhs) const
{
@@ -272,6 +375,7 @@ public:
character set is utf-8, we can safely assume that no
character starts with a zero byte.
*/
+ using std::min;
return memcmp(m_ptr, rhs->m_ptr, min(m_length, rhs->m_length));
}
@@ -290,19 +394,30 @@ public:
Get thread state name to be used in case when we have to
wait on resource identified by key.
*/
- const char * get_wait_state_name() const
+ const PSI_stage_info * get_wait_state_name() const
+ {
+ return & m_namespace_to_wait_state_name[(int)mdl_namespace()];
+ }
+ my_hash_value_type hash_value() const
+ {
+ return m_hash_value + mdl_namespace();
+ }
+ my_hash_value_type tc_hash_value() const
{
- return m_namespace_to_wait_state_name[(int)mdl_namespace()];
+ return m_hash_value;
}
private:
uint16 m_length;
uint16 m_db_name_length;
+ my_hash_value_type m_hash_value;
char m_ptr[MAX_MDLKEY_LENGTH];
- static const char * m_namespace_to_wait_state_name[NAMESPACE_END];
+ static PSI_stage_info m_namespace_to_wait_state_name[NAMESPACE_END];
private:
MDL_key(const MDL_key &); /* not implemented */
MDL_key &operator=(const MDL_key &); /* not implemented */
+ friend my_hash_value_type mdl_hash_function(const CHARSET_INFO *,
+ const uchar *, size_t);
};
@@ -406,17 +521,7 @@ public:
virtual bool inspect_edge(MDL_context *dest) = 0;
virtual ~MDL_wait_for_graph_visitor();
- MDL_wait_for_graph_visitor() :m_lock_open_count(0) {}
-public:
- /**
- XXX, hack: During deadlock search, we may need to
- inspect TABLE_SHAREs and acquire LOCK_open. Since
- LOCK_open is not a recursive mutex, count here how many
- times we "took" it (but only take and release once).
- Not using a native recursive mutex or rwlock in 5.5 for
- LOCK_open since it has significant performance impacts.
- */
- uint m_lock_open_count;
+ MDL_wait_for_graph_visitor() {}
};
/**
@@ -486,13 +591,15 @@ public:
MDL_context *get_ctx() const { return m_ctx; }
bool is_upgradable_or_exclusive() const
{
- return m_type == MDL_SHARED_NO_WRITE ||
+ return m_type == MDL_SHARED_UPGRADABLE ||
+ m_type == MDL_SHARED_NO_WRITE ||
m_type == MDL_SHARED_NO_READ_WRITE ||
m_type == MDL_EXCLUSIVE;
}
enum_mdl_type get_type() const { return m_type; }
MDL_lock *get_lock() const { return m_lock; }
- void downgrade_exclusive_lock(enum_mdl_type type);
+ MDL_key *get_key() const;
+ void downgrade_lock(enum_mdl_type type);
bool has_stronger_or_equal_type(enum_mdl_type type) const;
@@ -598,8 +705,10 @@ public:
bool set_status(enum_wait_status result_arg);
enum_wait_status get_status();
void reset_status();
- enum_wait_status timed_wait(THD *thd, struct timespec *abs_timeout,
- bool signal_timeout, const char *wait_state_name);
+ enum_wait_status timed_wait(MDL_context_owner *owner,
+ struct timespec *abs_timeout,
+ bool signal_timeout,
+ const PSI_stage_info *wait_state_name);
private:
/**
Condvar which is used for waiting until this context's pending
@@ -642,8 +751,9 @@ public:
bool try_acquire_lock(MDL_request *mdl_request);
bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout);
bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout);
- bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
- ulong lock_wait_timeout);
+ bool upgrade_shared_lock(MDL_ticket *mdl_ticket,
+ enum_mdl_type new_type,
+ ulong lock_wait_timeout);
bool clone_ticket(MDL_request *mdl_request);
@@ -653,6 +763,7 @@ public:
bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
const char *db, const char *name,
enum_mdl_type mdl_type);
+ unsigned long get_lock_owner(MDL_key *mdl_key);
bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
@@ -677,7 +788,7 @@ public:
void release_transactional_locks();
void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
- inline THD *get_thd() const { return m_thd; }
+ MDL_context_owner *get_owner() { return m_owner; }
/** @pre Only valid if we started waiting for lock. */
inline uint get_deadlock_weight() const
@@ -690,7 +801,7 @@ public:
already has received some signal or closed
signal slot.
*/
- void init(THD *thd_arg) { m_thd= thd_arg; }
+ void init(MDL_context_owner *arg) { m_owner= arg; }
void set_needs_thr_lock_abort(bool needs_thr_lock_abort)
{
@@ -721,9 +832,9 @@ private:
Lists of MDL tickets:
---------------------
The entire set of locks acquired by a connection can be separated
- in three subsets according to their: locks released at the end of
- statement, at the end of transaction and locks are released
- explicitly.
+ in three subsets according to their duration: locks released at
+ the end of statement, at the end of transaction and locks are
+ released explicitly.
Statement and transactional locks are locks with automatic scope.
They are accumulated in the course of a transaction, and released
@@ -732,11 +843,12 @@ private:
locks). They must not be (and never are) released manually,
i.e. with release_lock() call.
- Locks with explicit duration are taken for locks that span
+ Tickets with explicit duration are taken for locks that span
multiple transactions or savepoints.
These are: HANDLER SQL locks (HANDLER SQL is
transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
- under LOCK TABLES, and the locked tables stay locked), and
+ under LOCK TABLES, and the locked tables stay locked), user level
+ locks (GET_LOCK()/RELEASE_LOCK() functions) and
locks implementing "global read lock".
Statement/transactional locks are always prepended to the
@@ -745,20 +857,19 @@ private:
a savepoint, we start popping and releasing tickets from the
front until we reach the last ticket acquired after the savepoint.
- Locks with explicit duration stored are not stored in any
+ Locks with explicit duration are not stored in any
particular order, and among each other can be split into
- three sets:
+ four sets:
- [LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
+ [LOCK TABLES locks] [USER locks] [HANDLER locks] [GLOBAL READ LOCK locks]
The following is known about these sets:
- * GLOBAL READ LOCK locks are always stored after LOCK TABLES
- locks and after HANDLER locks. This is because one can't say
- SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
- if one has locked tables. One can, however, LOCK TABLES
- after having entered the read only mode. Note, that
- subsequent LOCK TABLES statement will unlock the previous
+ * GLOBAL READ LOCK locks are always stored last.
+ This is because one can't say SET GLOBAL read_only=1 or
+ FLUSH TABLES WITH READ LOCK if one has locked tables. One can,
+ however, LOCK TABLES after having entered the read only mode.
+ Note, that subsequent LOCK TABLES statement will unlock the previous
set of tables, but not the GRL!
There are no HANDLER locks after GRL locks because
SET GLOBAL read_only performs a FLUSH TABLES WITH
@@ -770,7 +881,7 @@ private:
involved schemas and global intention exclusive lock.
*/
Ticket_list m_tickets[MDL_DURATION_END];
- THD *m_thd;
+ MDL_context_owner *m_owner;
/**
TRUE - if for this context we will break protocol and try to
acquire table-level locks while having only S lock on
@@ -807,8 +918,11 @@ private:
MDL_ticket **out_ticket);
public:
+ THD *get_thd() const { return m_owner->get_thd(); }
void find_deadlock();
+ ulong get_thread_id() const { return thd_get_thread_id(get_thd()); }
+
bool visit_subgraph(MDL_wait_for_graph_visitor *dvisitor);
/** Inform the deadlock detector there is an edge in the wait-for graph. */
@@ -837,26 +951,26 @@ public:
private:
MDL_context(const MDL_context &rhs); /* not implemented */
MDL_context &operator=(MDL_context &rhs); /* not implemented */
+
+ /* metadata_lock_info plugin */
+ friend int i_s_metadata_lock_info_fill_row(MDL_ticket*, void*);
};
void mdl_init();
void mdl_destroy();
+extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd);
-/*
- Functions in the server's kernel used by metadata locking subsystem.
-*/
-
-extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
- bool needs_thr_lock_abort);
-extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
- mysql_mutex_t *mutex, const char *msg);
-extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
+/**
+ Check if a connection in question is no longer connected.
-#ifndef DBUG_OFF
-extern mysql_mutex_t LOCK_open;
-#endif
+ @details
+ Replication apply thread is always connected. Otherwise,
+ does a poll on the associated socket to check if the client
+ is gone.
+*/
+extern "C" int thd_is_connected(MYSQL_THD thd);
/*
@@ -867,9 +981,20 @@ extern ulong mdl_locks_cache_size;
static const ulong MDL_LOCKS_CACHE_SIZE_DEFAULT = 1024;
/*
+ Start-up parameter for the number of partitions of the hash
+ containing all the MDL_lock objects and a constant for
+ its default value.
+*/
+extern ulong mdl_locks_hash_partitions;
+static const ulong MDL_LOCKS_HASH_PARTITIONS_DEFAULT = 8;
+
+/*
Metadata locking subsystem tries not to grant more than
max_write_lock_count high-prio, strong locks successively,
to avoid starving out weak, low-prio locks.
*/
extern "C" ulong max_write_lock_count;
+
+extern MYSQL_PLUGIN_IMPORT
+int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg);
#endif