From: Tor Didriksen Date: January 27 2011 3:31pm Subject: bzr commit into mysql-trunk branch (tor.didriksen:3562) Bug#59309 List-Archive: http://lists.mysql.com/commits/129785 X-Bug: 59309 Message-Id: <20110127153125.B8EA933FB@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0846326611586309509==" --===============0846326611586309509== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/didrik/repo/trunk-bug59309-gtest-thd/ based on revid:tor.didriksen@stripped 3562 Tor Didriksen 2011-01-27 Bug #59309 Cleanup MDL - THD interface Define an abstract interface MDL_context_owner which is implemented by THD and the unit test classes. This allows us to separate MDL from the THD and the rest of the server code, and do standalone unit testing of the MDL module. @ sql/mdl.cc Use m_owner rather than thd when notifying about waiting events. @ sql/mdl.h Introduce a new interface (abstract class) MDL_context_owner. Remove declaration of the free functions used previously. @ sql/sql_base.cc Rename mysql_notify_thread_having_shared_lock to THD::notify_shared_lock and move it to sql_class.cc @ sql/sql_class.cc Rename mysql_notify_thread_having_shared_lock to THD::notify_shared_lock and move it here. @ sql/sql_class.h THD now implements the MDL_context_owner interface. @ sql/sql_insert.cc Update comment about notify_shared_lock @ unittest/gunit/mdl-t.cc Remove mock free-functions. Let the test fixture class and the test thread class implement MDL_context_owner @ unittest/gunit/mdl_mytap-t.cc Remove mock free-functions. Let the test fixture class and the test thread class implement MDL_context_owner @ unittest/gunit/test_mdl_context_owner.h Common implementation of MDL_context_owner for the two MDL unit tests. added: unittest/gunit/test_mdl_context_owner.h modified: sql/mdl.cc sql/mdl.h sql/sql_base.cc sql/sql_class.cc sql/sql_class.h sql/sql_insert.cc unittest/gunit/mdl-t.cc unittest/gunit/mdl_mytap-t.cc === modified file 'sql/mdl.cc' --- a/sql/mdl.cc 2010-11-19 12:34:44 +0000 +++ b/sql/mdl.cc 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2011, 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 @@ -731,7 +731,8 @@ void MDL_map::remove(MDL_lock *lock) */ MDL_context::MDL_context() - : m_thd(NULL), + : + m_owner(NULL), m_needs_thr_lock_abort(FALSE), m_waiting_for(NULL) { @@ -950,6 +951,7 @@ void MDL_wait::reset_status() /** Wait for the status to be assigned to this wait slot. + @param owner MDL context owner. @param abs_timeout Absolute time after which waiting should stop. @param set_status_on_timeout TRUE - If in case of timeout waiting context should close the wait slot by @@ -961,7 +963,7 @@ void MDL_wait::reset_status() */ MDL_wait::enum_wait_status -MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout, +MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout, bool set_status_on_timeout, const char *wait_state_name) { const char *old_msg; @@ -970,10 +972,9 @@ MDL_wait::timed_wait(THD *thd, struct ti mysql_mutex_lock(&m_LOCK_wait_status); - old_msg= thd_enter_cond(thd, &m_COND_wait_status, &m_LOCK_wait_status, - wait_state_name); - - while (!m_wait_status && !thd_killed(thd) && + old_msg= owner->enter_cond(&m_COND_wait_status, &m_LOCK_wait_status, + wait_state_name); + while (!m_wait_status && !owner->is_killed() && wait_result != ETIMEDOUT && wait_result != ETIME) wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status, abs_timeout); @@ -992,14 +993,14 @@ MDL_wait::timed_wait(THD *thd, struct ti false, which means that the caller intends to restart the wait. */ - if (thd_killed(thd)) + if (owner->is_killed()) m_wait_status= KILLED; else if (set_status_on_timeout) m_wait_status= TIMEOUT; } result= m_wait_status; - thd_exit_cond(thd, old_msg); + owner->exit_cond(old_msg); return result; } @@ -1696,9 +1697,9 @@ void MDL_object_lock::notify_conflicting lock or some other non-MDL resource we might need to wake it up by calling code outside of MDL. */ - mysql_notify_thread_having_shared_lock(ctx->get_thd(), - conflicting_ctx->get_thd(), - conflicting_ctx->get_needs_thr_lock_abort()); + ctx->get_owner()-> + notify_shared_lock(conflicting_ctx->get_owner(), + conflicting_ctx->get_needs_thr_lock_abort()); } } } @@ -1728,9 +1729,9 @@ void MDL_scoped_lock::notify_conflicting insert delayed. We need to kill such threads in order to get global shared lock. We do this my calling code outside of MDL. */ - mysql_notify_thread_having_shared_lock(ctx->get_thd(), - conflicting_ctx->get_thd(), - conflicting_ctx->get_needs_thr_lock_abort()); + ctx->get_owner()-> + notify_shared_lock(conflicting_ctx->get_owner(), + conflicting_ctx->get_needs_thr_lock_abort()); } } } @@ -1797,7 +1798,7 @@ MDL_context::acquire_lock(MDL_request *m will_wait_for(ticket); /* There is a shared or exclusive lock on the object. */ - DEBUG_SYNC(m_thd, "mdl_acquire_lock_wait"); + DEBUG_SYNC(get_thd(), "mdl_acquire_lock_wait"); find_deadlock(); @@ -1810,7 +1811,7 @@ MDL_context::acquire_lock(MDL_request *m while (cmp_timespec(abs_shortwait, abs_timeout) <= 0) { /* abs_timeout is far away. Wait a short while and notify locks. */ - wait_status= m_wait.timed_wait(m_thd, &abs_shortwait, FALSE, + wait_status= m_wait.timed_wait(m_owner, &abs_shortwait, FALSE, mdl_request->key.get_wait_state_name()); if (wait_status != MDL_wait::EMPTY) @@ -1822,11 +1823,11 @@ MDL_context::acquire_lock(MDL_request *m set_timespec(abs_shortwait, 1); } if (wait_status == MDL_wait::EMPTY) - wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE, + wait_status= m_wait.timed_wait(m_owner, &abs_timeout, TRUE, mdl_request->key.get_wait_state_name()); } else - wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE, + wait_status= m_wait.timed_wait(m_owner, &abs_timeout, TRUE, mdl_request->key.get_wait_state_name()); done_waiting_for(); === modified file 'sql/mdl.h' --- a/sql/mdl.h 2010-11-22 11:20:49 +0000 +++ b/sql/mdl.h 2011-01-27 15:31:18 +0000 @@ -1,6 +1,6 @@ #ifndef MDL_H #define MDL_H -/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2011, 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 @@ -35,6 +35,41 @@ class MDL_context; class MDL_lock; class MDL_ticket; + +/** + 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() {} + + /** + @see THD::enter_cond() and THD::exit_cond() + */ + virtual const char* enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex, + const char* msg) = 0; + virtual void exit_cond(const char* old_msg) = 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. @@ -593,7 +628,8 @@ 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, + enum_wait_status timed_wait(MDL_context_owner *owner, + struct timespec *abs_timeout, bool signal_timeout, const char *wait_state_name); private: /** @@ -672,7 +708,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 @@ -685,7 +721,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) { @@ -765,7 +801,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 @@ -794,6 +830,7 @@ private: */ MDL_wait_for_subgraph *m_waiting_for; private: + THD *get_thd() const { return m_owner->get_thd(); } MDL_ticket *find_ticket(MDL_request *mdl_req, enum_mdl_duration *duration); void release_locks_stored_before(enum_mdl_duration duration, MDL_ticket *sentinel); @@ -839,16 +876,6 @@ void mdl_init(); void mdl_destroy(); -/* - 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); - #ifndef DBUG_OFF extern mysql_mutex_t LOCK_open; #endif === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2011-01-11 11:45:02 +0000 +++ b/sql/sql_base.cc 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, 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 @@ -8667,72 +8667,6 @@ void tdc_flush_unused_tables() /** - A callback to the server internals that is used to address - special cases of the locking protocol. - Invoked when acquiring an exclusive lock, for each thread that - has a conflicting shared metadata lock. - - This function: - - aborts waiting of the thread on a data lock, to make it notice - the pending exclusive lock and back off. - - if the thread is an INSERT DELAYED thread, sends it a KILL - signal to terminate it. - - @note This function does not wait for the thread to give away its - locks. Waiting is done outside for all threads at once. - - @param thd Current thread context - @param in_use The thread to wake up - @param needs_thr_lock_abort Indicates that to wake up thread - this call needs to abort its waiting - on table-level lock. - - @retval TRUE if the thread was woken up - @retval FALSE otherwise. - - @note It is one of two places where border between MDL and the - rest of the server is broken. -*/ - -bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, - bool needs_thr_lock_abort) -{ - bool signalled= FALSE; - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - !in_use->killed) - { - in_use->killed= THD::KILL_CONNECTION; - mysql_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - mysql_cond_broadcast(in_use->mysys_var->current_cond); - mysql_mutex_unlock(&in_use->mysys_var->mutex); - signalled= TRUE; - } - - if (needs_thr_lock_abort) - { - mysql_mutex_lock(&in_use->LOCK_thd_data); - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* - Check for TABLE::needs_reopen() is needed since in some places we call - handler::close() for table instance (and set TABLE::db_stat to 0) - and do not remove such instances from the THD::open_tables - for some time, during which other thread can see those instances - (e.g. see partitioning code). - */ - if (!thd_table->needs_reopen()) - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - } - mysql_mutex_unlock(&in_use->LOCK_thd_data); - } - return signalled; -} - - -/** Remove all or some (depending on parameter) instances of TABLE and TABLE_SHARE from the table definition cache. === modified file 'sql/sql_class.cc' --- a/sql/sql_class.cc 2011-01-27 13:46:28 +0000 +++ b/sql/sql_class.cc 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, 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 @@ -58,6 +58,7 @@ #include "debug_sync.h" #include "sql_parse.h" // is_update_query #include "sql_callback.h" +#include "lock.h" /* The following is used to initialise Table_ident with a internal @@ -1322,6 +1323,45 @@ void THD::disconnect() } +bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, + bool needs_thr_lock_abort) +{ + THD *in_use= ctx_in_use->get_thd(); + bool signalled= FALSE; + if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && + !in_use->killed) + { + in_use->killed= THD::KILL_CONNECTION; + mysql_mutex_lock(&in_use->mysys_var->mutex); + if (in_use->mysys_var->current_cond) + mysql_cond_broadcast(in_use->mysys_var->current_cond); + mysql_mutex_unlock(&in_use->mysys_var->mutex); + signalled= TRUE; + } + + if (needs_thr_lock_abort) + { + mysql_mutex_lock(&in_use->LOCK_thd_data); + for (TABLE *thd_table= in_use->open_tables; + thd_table ; + thd_table= thd_table->next) + { + /* + Check for TABLE::needs_reopen() is needed since in some places we call + handler::close() for table instance (and set TABLE::db_stat to 0) + and do not remove such instances from the THD::open_tables + for some time, during which other thread can see those instances + (e.g. see partitioning code). + */ + if (!thd_table->needs_reopen()) + signalled|= mysql_lock_abort_for_thread(this, thd_table); + } + mysql_mutex_unlock(&in_use->LOCK_thd_data); + } + return signalled; +} + + /* Remember the location of thread info, the structure needed for sql_alloc() and the structure for the net buffer === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2010-12-29 00:38:59 +0000 +++ b/sql/sql_class.h 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, 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 @@ -1475,7 +1475,8 @@ extern "C" void my_message_sql(uint erro */ class THD :public Statement, - public Open_tables_state + public Open_tables_state, + public MDL_context_owner { public: MDL_context mdl_context; @@ -2287,6 +2288,8 @@ public: int errcode); #endif + // Begin implementation of MDL_context_owner interface. + /* For enter_cond() / exit_cond() to work the mutex must be got before enter_cond(); this mutex is then released by exit_cond(). @@ -2318,6 +2321,39 @@ public: mysql_mutex_unlock(&mysys_var->mutex); return; } + + virtual int is_killed() { return killed; } + virtual THD* get_thd() { return this; } + + /** + A callback to the server internals that is used to address + special cases of the locking protocol. + Invoked when acquiring an exclusive lock, for each thread that + has a conflicting shared metadata lock. + + This function: + - aborts waiting of the thread on a data lock, to make it notice + the pending exclusive lock and back off. + - if the thread is an INSERT DELAYED thread, sends it a KILL + signal to terminate it. + + @note This function does not wait for the thread to give away its + locks. Waiting is done outside for all threads at once. + + @param ctx_in_use The MDL context owner (thread) to wake up. + @param needs_thr_lock_abort Indicates that to wake up thread + this call needs to abort its waiting + on table-level lock. + + @retval TRUE if the thread was woken up + @retval FALSE otherwise. + */ + virtual bool notify_shared_lock(MDL_context_owner *ctx_in_use, + bool needs_thr_lock_abort); + + // End implementation of MDL_context_owner interface. + + inline time_t query_start() { query_start_used=1; return start_time; } inline void set_time() { === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2011-01-21 11:30:47 +0000 +++ b/sql/sql_insert.cc 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, 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 @@ -2242,7 +2242,7 @@ TABLE *Delayed_insert::get_local_table(T The thread could be killed with an error message if di->handle_inserts() or di->open_and_lock_table() fails. The thread could be killed without an error message if - killed using mysql_notify_thread_having_shared_lock() or + killed using THD::notify_shared_lock() or kill_delayed_threads_for_table(). */ if (!thd.is_error() || thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN) === modified file 'unittest/gunit/mdl-t.cc' --- a/unittest/gunit/mdl-t.cc 2010-12-23 11:03:09 +0000 +++ b/unittest/gunit/mdl-t.cc 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Sun Microsystems, Inc. +/* Copyright (c) 2009, 2011, 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 @@ -15,9 +15,9 @@ /** This is a unit test for the 'meta data locking' classes. - It is written to illustrate how we can use googletest for unit testing + It is written to illustrate how we can use Google Test for unit testing of MySQL code. - For documentation on googletest, see http://code.google.com/p/googletest/ + For documentation on Google Test, see http://code.google.com/p/googletest/ and the contained wiki pages GoogleTestPrimer and GoogleTestAdvancedGuide. The code below should hopefully be (mostly) self-explanatory. */ @@ -32,32 +32,13 @@ #include "thr_malloc.h" #include "thread_utils.h" +#include "test_mdl_context_owner.h" pthread_key(MEM_ROOT**,THR_MALLOC); pthread_key(THD*, THR_THD); mysql_mutex_t LOCK_open; uint opt_debug_sync_timeout= 0; -static mysql_mutex_t *current_mutex= NULL; -extern "C" -const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, - mysql_mutex_t *mutex, const char *msg) -{ - current_mutex= mutex; - return NULL; -} - -extern "C" -void thd_exit_cond(MYSQL_THD thd, const char *old_msg) -{ - mysql_mutex_unlock(current_mutex); -} - -extern "C" int thd_killed(const MYSQL_THD thd) -{ - return 0; -} - /* A mock error handler. */ @@ -76,50 +57,6 @@ extern "C" void sql_alloc_error_handler( ADD_FAILURE(); } -namespace { -bool notify_thread(THD*); -} - -/* - We need to mock away this global function, because the real version - pulls in a lot of dependencies. - (The @note for the real version of this function indicates that the - coupling between THD and MDL is too tight.) - @retval TRUE if the thread was woken up - @retval FALSE otherwise. -*/ -bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, - bool needs_thr_lock_abort) -{ - if (in_use != NULL) - return notify_thread(in_use); - return FALSE; -} - -/* - Mock away this function as well, with an empty function. - @todo didrik: Consider verifying that the MDL module actually calls - this with correct arguments. -*/ -void mysql_ha_flush(THD *) -{ - DBUG_PRINT("mysql_ha_flush", ("mock version")); -} - -/* - We need to mock away this global function, the real version pulls in - too many dependencies. - */ -extern "C" const char *set_thd_proc_info(void *thd, const char *info, - const char *calling_function, - const char *calling_file, - const unsigned int calling_line) -{ - DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, - (info != NULL) ? info : "(null)")); - return info; -} - /* Mock away this global function. We don't need DEBUG_SYNC functionality in a unit test. @@ -148,12 +85,11 @@ const ulong zero_timeout= 0; const ulong long_timeout= (ulong) 3600L*24L*365L; -class MDLTest : public ::testing::Test +class MDLTest : public ::testing::Test, public Test_MDL_context_owner { protected: MDLTest() - : m_thd(NULL), - m_null_ticket(NULL), + : m_null_ticket(NULL), m_null_request(NULL) { } @@ -167,7 +103,7 @@ protected: { expected_error= 0; mdl_init(); - m_mdl_context.init(m_thd); + m_mdl_context.init(this); EXPECT_FALSE(m_mdl_context.has_locks()); m_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION); @@ -179,10 +115,15 @@ protected: mdl_destroy(); } + virtual bool notify_shared_lock(MDL_context_owner *in_use, + bool needs_thr_lock_abort) + { + return in_use->notify_shared_lock(NULL, needs_thr_lock_abort); + } + // A utility member for testing single lock requests. void test_one_simple_shared_lock(enum_mdl_type lock_type); - THD *m_thd; const MDL_ticket *m_null_ticket; const MDL_request *m_null_request; MDL_context m_mdl_context; @@ -199,7 +140,7 @@ private: The two notifications are for synchronizing with the main thread. Does *not* take ownership of the notifications. */ -class MDL_thread : public Thread +class MDL_thread : public Thread, public Test_MDL_context_owner { public: MDL_thread(const char *table_name, @@ -212,8 +153,7 @@ public: m_release_locks(release_locks), m_ignore_notify(false) { - m_thd= reinterpret_cast(this); // See notify_thread below. - m_mdl_context.init(m_thd); + m_mdl_context.init(this); } ~MDL_thread() @@ -224,8 +164,12 @@ public: virtual void run(); void ignore_notify() { m_ignore_notify= true; } - bool notify() + virtual bool notify_shared_lock(MDL_context_owner *in_use, + bool needs_thr_lock_abort) { + if (in_use) + return in_use->notify_shared_lock(NULL, needs_thr_lock_abort); + if (m_ignore_notify) return false; m_release_locks->notify(); @@ -238,19 +182,10 @@ private: Notification *m_lock_grabbed; Notification *m_release_locks; bool m_ignore_notify; - THD *m_thd; MDL_context m_mdl_context; }; -// Admittedly an ugly hack, to avoid pulling in the THD in this unit test. -bool notify_thread(THD *thd) -{ - MDL_thread *thread = (MDL_thread*) thd; - return thread->notify(); -} - - void MDL_thread::run() { MDL_request request; @@ -277,7 +212,7 @@ void MDL_thread::run() m_mdl_context.release_transactional_locks(); } -// googletest recommends DeathTest suffix for classes use in death tests. +// Google Test recommends DeathTest suffix for classes use in death tests. typedef MDLTest MDLDeathTest; @@ -438,9 +373,8 @@ TEST_F(MDLTest, TwoShared) */ TEST_F(MDLTest, SharedLocksBetweenContexts) { - THD *thd2= (THD*) this; MDL_context mdl_context2; - mdl_context2.init(thd2); + mdl_context2.init(this); MDL_request request_2; m_request.init(MDL_key::TABLE, db_name, table_name1, MDL_SHARED, MDL_TRANSACTION); === modified file 'unittest/gunit/mdl_mytap-t.cc' --- a/unittest/gunit/mdl_mytap-t.cc 2010-12-23 11:03:09 +0000 +++ b/unittest/gunit/mdl_mytap-t.cc 2011-01-27 15:31:18 +0000 @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Sun Microsystems, Inc. +/* Copyright (c) 2009, 2011, 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 @@ -14,9 +14,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** - This is a port of the corresponding mdl_test.cc (written for googletest) + This is a port of the corresponding mdl_test.cc (written for Google Test) to mytap. Do a 'tkdiff mdl-t.cc mdl_mytap-t.cc' to see the differences. - In order to illustrate (some of) the features of googletest, I have + In order to illustrate (some of) the features of Google Test, I have added some extensions below, notably support for reporting of line numbers in case of failures. */ @@ -35,34 +35,14 @@ #include "thr_malloc.h" #include "thread_utils.h" +#include "test_mdl_context_owner.h" pthread_key(MEM_ROOT**,THR_MALLOC); pthread_key(THD*, THR_THD); mysql_mutex_t LOCK_open; uint opt_debug_sync_timeout= 0; -static mysql_mutex_t *current_mutex= NULL; -extern "C" -const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, - mysql_mutex_t *mutex, const char *msg) -{ - current_mutex= mutex; - return NULL; -} - -extern "C" -void thd_exit_cond(MYSQL_THD thd, const char *old_msg) -{ - mysql_mutex_unlock(current_mutex); -} - -extern "C" int thd_killed(const MYSQL_THD thd) -{ - return 0; -} - - -// Reimplemented some macros from googletest, so that the tests below +// Reimplemented some macros from Google Test, so that the tests below // could be kept unchanged. No support for streaming of user messages // in this simplified version. void print_message(const char* file, int line, const char* message) @@ -132,50 +112,6 @@ extern "C" void sql_alloc_error_handler( FAIL(); } -namespace { -bool notify_thread(THD*); -} - -/* - We need to mock away this global function, because the real version - pulls in a lot of dependencies. - (The @note for the real version of this function indicates that the - coupling between THD and MDL is too tight.) - @retval TRUE if the thread was woken up - @retval FALSE otherwise. -*/ -bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, - bool needs_thr_lock_abort) -{ - if (in_use != NULL) - return notify_thread(in_use); - return FALSE; -} - -/* - Mock away this function as well, with an empty function. - @todo didrik: Consider verifying that the MDL module actually calls - this with correct arguments. -*/ -void mysql_ha_flush(THD *) -{ - DBUG_PRINT("mysql_ha_flush", ("mock version")); -} - -/* - We need to mock away this global function, the real version pulls in - too many dependencies. - */ -extern "C" const char *set_thd_proc_info(void *thd, const char *info, - const char *calling_function, - const char *calling_file, - const unsigned int calling_line) -{ - DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, - (info != NULL) ? info : "(null)")); - return info; -} - /* Mock away this global function. We don't need DEBUG_SYNC functionality in a unit test. @@ -204,7 +140,7 @@ const ulong zero_timeout= 0; const ulong long_timeout= (ulong) 3600L*24L*365L; -class MDLTest +class MDLTest : public Test_MDL_context_owner { public: // Utility function to run one test case. @@ -216,8 +152,7 @@ public: protected: MDLTest() - : m_thd(NULL), - m_null_ticket(NULL), + : m_null_ticket(NULL), m_null_request(NULL) { } @@ -231,7 +166,7 @@ protected: { expected_error= 0; mdl_init(); - m_mdl_context.init(m_thd); + m_mdl_context.init(this); EXPECT_FALSE(m_mdl_context.has_locks()); m_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION); @@ -243,6 +178,12 @@ protected: mdl_destroy(); } + virtual bool notify_shared_lock(MDL_context_owner *in_use, + bool needs_thr_lock_abort) + { + return in_use->notify_shared_lock(NULL, needs_thr_lock_abort); + } + // A utility member for testing single lock requests. void test_one_simple_shared_lock(enum_mdl_type lock_type); @@ -265,7 +206,6 @@ protected: void ConcurrentExclusiveShared(); void ConcurrentUpgrade(); - THD *m_thd; const MDL_ticket *m_null_ticket; const MDL_request *m_null_request; MDL_context m_mdl_context; @@ -282,7 +222,7 @@ private: The two notifications are for synchronizing with the main thread. Does *not* take ownership of the notifications. */ -class MDL_thread : public Thread +class MDL_thread : public Thread, public Test_MDL_context_owner { public: MDL_thread(const char *table_name, @@ -295,8 +235,7 @@ public: m_release_locks(release_locks), m_ignore_notify(false) { - m_thd= reinterpret_cast(this); // See notify_thread below. - m_mdl_context.init(m_thd); + m_mdl_context.init(this); } ~MDL_thread() @@ -307,8 +246,12 @@ public: virtual void run(); void ignore_notify() { m_ignore_notify= true; } - bool notify() + virtual bool notify_shared_lock(MDL_context_owner *in_use, + bool needs_thr_lock_abort) { + if (in_use) + return in_use->notify_shared_lock(NULL, needs_thr_lock_abort); + if (m_ignore_notify) return false; m_release_locks->notify(); @@ -321,19 +264,10 @@ private: Notification *m_lock_grabbed; Notification *m_release_locks; bool m_ignore_notify; - THD *m_thd; MDL_context m_mdl_context; }; -// Admittedly an ugly hack, to avoid pulling in the THD in this unit test. -bool notify_thread(THD *thd) -{ - MDL_thread *thread = (MDL_thread*) thd; - return thread->notify(); -} - - void MDL_thread::run() { MDL_request request; @@ -360,7 +294,7 @@ void MDL_thread::run() m_mdl_context.release_transactional_locks(); } -// googletest recommends DeathTest suffix for classes use in death tests. +// Google Test recommends DeathTest suffix for classes use in death tests. typedef MDLTest MDLDeathTest; // Our own (simplified) version of the TEST_F macro. @@ -506,9 +440,8 @@ TEST_F(MDLTest, TwoShared) */ TEST_F(MDLTest, SharedLocksBetweenContexts) { - THD *thd2= (THD*) this; MDL_context mdl_context2; - mdl_context2.init(thd2); + mdl_context2.init(this); MDL_request request_2; m_request.init(MDL_key::TABLE, db_name, table_name1, MDL_SHARED, MDL_TRANSACTION); === added file 'unittest/gunit/test_mdl_context_owner.h' --- a/unittest/gunit/test_mdl_context_owner.h 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/test_mdl_context_owner.h 2011-01-27 15:31:18 +0000 @@ -0,0 +1,48 @@ +/* Copyright (c) 2011, 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef TEST_MDL_CONTEXT_OWNER_INCLUDED +#define TEST_MDL_CONTEXT_OWNER_INCLUDED + +#include +#include + +class Test_MDL_context_owner : public MDL_context_owner +{ +public: + Test_MDL_context_owner() + : m_current_mutex(NULL) + {} + virtual const char* enter_cond(mysql_cond_t *cond, + mysql_mutex_t* mutex, + const char* msg) + { + m_current_mutex= mutex; + return NULL; + } + + virtual void exit_cond(const char* old_msg) + { + mysql_mutex_unlock(m_current_mutex); + } + + virtual int is_killed() { return 0; } + virtual THD* get_thd() { return NULL; } + +private: + mysql_mutex_t *m_current_mutex; +}; + +#endif // TEST_MDL_CONTEXT_OWNER_INCLUDED --===============0846326611586309509== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/tor.didriksen@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: tor.didriksen@stripped\ # qo3gyazm32xcwsw8 # target_branch: file:///export/home/didrik/repo/trunk-bug59309-gtest-\ # thd/ # testament_sha1: c2cfdf994f2146689cd7eafa9f42f16d9a5a7ce4 # timestamp: 2011-01-27 16:31:25 +0100 # base_revision_id: tor.didriksen@stripped\ # 9cakw9celrequj4i # # Begin bundle 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 Iww= --===============0846326611586309509==--