File: debug_sync-t.cc

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (182 lines) | stat: -rw-r--r-- 6,206 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* Copyright (c) 2013, 2025, Oracle and/or its affiliates.

   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 designed to work 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 either included with
   the program or referenced in the documentation.

   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() = default;

  void SetUp() override {
    // set debug sync timeout of 60 seconds.
    opt_debug_sync_timeout = 60;
    debug_sync_init();
  }

  void TearDown() override {
    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