From: Tor Didriksen Date: January 10 2011 8:41am Subject: bzr commit into mysql-trunk branch (tor.didriksen:3471) Bug#59309 List-Archive: http://lists.mysql.com/commits/128251 X-Bug: 59309 Message-Id: <20110110084154.177493127@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============7493318459931998999==" --===============7493318459931998999== 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:vasil.dimov@stripped 3471 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 --===============7493318459931998999== 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\ # p7olr0btbt9zmnu3 # target_branch: file:///export/home/didrik/repo/trunk-bug59309-gtest-\ # thd/ # testament_sha1: 8521a26475d7f6096609e193738065a5981e122a # timestamp: 2011-01-10 09:41:54 +0100 # base_revision_id: vasil.dimov@stripped\ # 8qygun7nuaqf9d3u # # Begin bundle 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 --===============7493318459931998999==--