You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
6.0 KiB
181 lines
6.0 KiB
/* Copyright (c) 2013, 2017, 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, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
/**
|
|
This is a unit test written to demonstrate a race issue in debug_sync
|
|
functionality which resulted in wait thread getting timed out. This
|
|
happens because we use a single global variable to identify the thread
|
|
that has signalled. Before the wait could process the signal from the
|
|
signal thread, another signal thread can overwrite this global signal
|
|
value thus making the wait thread to lose this signal and timeout
|
|
waiting for a signal that it has already lost.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <string>
|
|
|
|
#include "my_dbug.h"
|
|
#include "my_inttypes.h"
|
|
#include "sql/debug_sync.h"
|
|
#include "unittest/gunit/test_utils.h"
|
|
#include "unittest/gunit/thread_utils.h"
|
|
|
|
#if defined(ENABLED_DEBUG_SYNC)
|
|
extern uchar *debug_sync_value_ptr(THD *thd);
|
|
|
|
namespace debug_sync_unittest {
|
|
|
|
using my_testing::Mock_error_handler;
|
|
using my_testing::Server_initializer;
|
|
using thread::Notification;
|
|
using thread::Thread;
|
|
|
|
class DebugSyncTest : public ::testing::Test {
|
|
protected:
|
|
DebugSyncTest() {}
|
|
|
|
void SetUp() {
|
|
// set debug sync timeout of 60 seconds.
|
|
opt_debug_sync_timeout = 60;
|
|
debug_sync_init();
|
|
}
|
|
|
|
void TearDown() {
|
|
debug_sync_end();
|
|
opt_debug_sync_timeout = 0;
|
|
}
|
|
};
|
|
|
|
/*
|
|
Set up a debug sync action thread. Depending on type of action, the thread
|
|
serves different purpose. In the case where it involves wait_for action, it
|
|
serves the purpose of a waiting thread that wait at a 'sync_point' in it's
|
|
thread of control for named signal event. The debug sync action could
|
|
be a signal event, in which case it assumes the role of signalling thread
|
|
that signals an event identified by signal name to wake a waiting thread.
|
|
*/
|
|
class DebugSyncThread : public Thread {
|
|
public:
|
|
DebugSyncThread(Notification *go, bool wait_thread, std::string action)
|
|
: m_go(go), m_wait_thread(wait_thread), m_action(action) {}
|
|
|
|
virtual void run() {
|
|
m_initializer.SetUp();
|
|
m_thd = m_initializer.thd();
|
|
m_go->wait_for_notification();
|
|
|
|
if (m_wait_thread) {
|
|
std::string sync_point("sync_point ");
|
|
std::string action_str = sync_point + m_action;
|
|
Mock_error_handler error_handler(m_thd, 0);
|
|
|
|
debug_sync_set_action(m_thd, action_str.c_str(), action_str.length());
|
|
wait_point();
|
|
|
|
// The above should not generate any warnings.
|
|
EXPECT_EQ(0, error_handler.handle_called());
|
|
} else {
|
|
debug_sync_set_action(m_thd, m_action.c_str(), m_action.length());
|
|
}
|
|
|
|
m_initializer.TearDown();
|
|
}
|
|
|
|
private:
|
|
Notification *m_go;
|
|
Server_initializer m_initializer;
|
|
THD *m_thd; // THD context required to hold debug_sync context.
|
|
bool m_wait_thread; // indicate if it is a wait thread.
|
|
std::string m_action; // indicate type of action WAIT_FOR or SIGNAL.
|
|
|
|
void wait_point() { DEBUG_SYNC(m_thd, "sync_point"); }
|
|
};
|
|
|
|
/*
|
|
Start a wait thread waiting for signal X1 and two signal threads
|
|
one signalling event X1 and other signalling an event X2. Notify
|
|
all of them to start at once.
|
|
*/
|
|
TEST_F(DebugSyncTest, DebugSyncSignalWaitTests) {
|
|
Notification go[3];
|
|
|
|
/* wait thread */
|
|
DebugSyncThread wait_thread(&go[0], true, std::string("WAIT_FOR X1"));
|
|
/* signal thread */
|
|
DebugSyncThread signal_thread1(&go[1], false, std::string("now signal X1"));
|
|
DebugSyncThread signal_thread2(&go[2], false, std::string("now signal X2"));
|
|
|
|
wait_thread.start();
|
|
signal_thread1.start();
|
|
signal_thread2.start();
|
|
|
|
// Notify all threads
|
|
go[0].notify();
|
|
go[1].notify();
|
|
go[2].notify();
|
|
|
|
// Wait for all threads to finish.
|
|
wait_thread.join();
|
|
signal_thread1.join();
|
|
signal_thread2.join();
|
|
}
|
|
|
|
// Test debug_sync_value_ptr
|
|
TEST_F(DebugSyncTest, DebugSyncValuesTest) {
|
|
std::string action;
|
|
Server_initializer server_initializer;
|
|
THD *thd;
|
|
|
|
server_initializer.SetUp();
|
|
thd = server_initializer.thd();
|
|
|
|
// Ensure that we have a empty signal list at startup.
|
|
EXPECT_STREQ("ON - signals: ''",
|
|
reinterpret_cast<char *>(debug_sync_value_ptr(thd)));
|
|
|
|
// Set up signalling actions and ensure the signals list reflect it.
|
|
action = "now signal x1";
|
|
debug_sync_set_action(thd, action.c_str(), action.length());
|
|
EXPECT_STREQ("ON - signals: 'x1'",
|
|
reinterpret_cast<char *>(debug_sync_value_ptr(thd)));
|
|
|
|
action = "now signal x2";
|
|
debug_sync_set_action(thd, action.c_str(), action.length());
|
|
EXPECT_STREQ("ON - signals: 'x1,x2'",
|
|
reinterpret_cast<char *>(debug_sync_value_ptr(thd)));
|
|
|
|
action = "now signal x3";
|
|
debug_sync_set_action(thd, action.c_str(), action.length());
|
|
EXPECT_STREQ("ON - signals: 'x1,x2,x3'",
|
|
reinterpret_cast<char *>(debug_sync_value_ptr(thd)));
|
|
|
|
// Ensure the signal list is empty after reset.
|
|
action = "reset";
|
|
debug_sync_set_action(thd, action.c_str(), action.length());
|
|
EXPECT_STREQ("ON - signals: ''",
|
|
reinterpret_cast<char *>(debug_sync_value_ptr(thd)));
|
|
|
|
server_initializer.TearDown();
|
|
}
|
|
|
|
} // namespace debug_sync_unittest
|
|
#endif
|
|
|