198 lines
5.6 KiB
C++
198 lines
5.6 KiB
C++
/* Copyright (c) 2013, 2015, 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., 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 "my_global.h"
|
|
#include <gtest/gtest.h>
|
|
#include "test_utils.h"
|
|
#include "thread_utils.h"
|
|
#include "debug_sync.h"
|
|
#include "my_dbug.h"
|
|
#include <string>
|
|
|
|
#if defined(ENABLED_DEBUG_SYNC)
|
|
extern uchar* debug_sync_value_ptr(THD* thd);
|
|
|
|
namespace debug_sync_unittest {
|
|
|
|
using thread::Notification;
|
|
using thread::Thread;
|
|
using my_testing::Server_initializer;
|
|
using my_testing::Mock_error_handler;
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
}
|
|
#endif
|