From: Tor Didriksen Date: January 6 2011 2:30pm Subject: bzr commit into mysql-trunk branch (tor.didriksen:3465) Bug#59309 List-Archive: http://lists.mysql.com/commits/128084 X-Bug: 59309 Message-Id: <20110106143041.3328C377A@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============7256620922116697686==" --===============7256620922116697686== 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:saikumar.v@stripped 3465 Tor Didriksen 2011-01-06 Bug #59309 Cleanup MDL - THD interface Define an abstract interface MDL_context_owner which is implemented by THD and the unit test classes. @ 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-06 14:30:37 +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-06 14:30:37 +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,37 @@ class MDL_context; class MDL_lock; class MDL_ticket; + +/** + An interface to separate the MDL classes from the THD, + so that we can do unit testing. + */ + +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; + + 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 +624,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 +704,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 +717,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 +797,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 +826,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 +872,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 2010-12-29 00:38:59 +0000 +++ b/sql/sql_base.cc 2011-01-06 14:30:37 +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 @@ -8662,72 +8662,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 2010-12-17 16:14:15 +0000 +++ b/sql/sql_class.cc 2011-01-06 14:30:37 +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 @@ -1321,6 +1322,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-06 14:30:37 +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 2010-12-22 13:23:59 +0000 +++ b/sql/sql_insert.cc 2011-01-06 14:30:37 +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 @@ -2239,7 +2239,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-06 14:30:37 +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-06 14:30:37 +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-06 14:30:37 +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 --===============7256620922116697686== 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\ # fguorafv55e1dos2 # target_branch: file:///export/home/didrik/repo/trunk-bug59309-gtest-\ # thd/ # testament_sha1: 6bc447c44f28c45b2f58305160f2cd6fe7c8c50f # timestamp: 2011-01-06 15:30:41 +0100 # base_revision_id: saikumar.v@stripped # # Begin bundle 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 --===============7256620922116697686==--