1 /* Copyright (c) 2013, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "my_config.h"
24 #include <gtest/gtest.h>
25 #include "tc_log.h"
26 #include "sql_class.h"
27 #include "test_utils.h"
28 #include "thread_utils.h"
29
30 #ifdef _WIN32
31 #include <process.h> // getpid
32 #endif
33
34 using my_testing::Server_initializer;
35
36 /**
37 Override msync/fsync, saves a *lot* of time during unit testing.
38 */
39
40 class TC_LOG_MMAP_no_msync : public TC_LOG_MMAP
41 {
42 protected:
do_msync_and_fsync(int fd,void * addr,size_t len,int flags)43 virtual int do_msync_and_fsync(int fd, void *addr, size_t len, int flags)
44 {
45 return 0;
46 }
47 };
48
49 /**
50 This class is a friend of TC_LOG_MMAP, so it needs to be outside the unittest
51 namespace.
52 */
53
54 class TCLogMMapTest : public ::testing::Test
55 {
56 public:
TCLogMMapTest()57 TCLogMMapTest()
58 : tc_log_mmap(NULL)
59 {
60 }
61
62
SetUp()63 virtual void SetUp()
64 {
65 initializer.SetUp();
66 total_ha_2pc= 2;
67 tc_heuristic_recover= TC_HEURISTIC_NOT_USED;
68 /*
69 Assign a transaction coordinator object to am
70 instance of TCLogMMapTest. This transaction coordinator
71 is shared among all threads are run.
72 */
73
74 tc_log_mmap= new TC_LOG_MMAP_no_msync();
75 // Make a slightly randomized name for the file,
76 // to avoid recovery from other runs.
77 char namebuff[FN_REFLEN];
78 my_snprintf(namebuff, FN_REFLEN,
79 "tc_log_mmap_test_%d", static_cast<int>(getpid()));
80 ASSERT_EQ(0, tc_log_mmap->open(namebuff));
81 }
82
83
TearDown()84 virtual void TearDown()
85 {
86 tc_log_mmap->close();
87 delete tc_log_mmap;
88 initializer.TearDown();
89 }
90
91
thd()92 THD *thd()
93 {
94 return initializer.thd();
95 }
96
97
98 /**
99 Run test case in single threaded environment.
100 This method uses THD value hold by the initializer data member.
101 */
102
testCommit(ulonglong xid)103 void testCommit(ulonglong xid)
104 {
105 testCommit(xid, thd());
106 }
107
108
109 /**
110 Run a test case with the THD supplied outside. This method is used
111 when there are several threads running the same test case. In this case
112 we need a dedicated THD object for every thread in order not to hit
113 upon the wrong condition when two threads free a memroot (indirectly by
114 calling thd->get_transaction()->cleanup()) for the same THD object.
115 */
116
testCommit(ulonglong xid,THD * thd_val)117 void testCommit(ulonglong xid, THD *thd_val)
118 {
119 XID_STATE *xid_state= thd_val->get_transaction()->xid_state();
120 xid_state->set_query_id(xid);
121 EXPECT_EQ(TC_LOG_MMAP::RESULT_SUCCESS, tc_log_mmap->commit(thd_val, true));
122 thd_val->get_transaction()->cleanup();
123 }
124
testLog(ulonglong xid)125 ulong testLog(ulonglong xid)
126 {
127 return tc_log_mmap->log_xid(xid);
128 }
129
testUnlog(ulong cookie,ulonglong xid)130 void testUnlog(ulong cookie, ulonglong xid)
131 {
132 tc_log_mmap->unlog(cookie, xid);
133 }
134
135 protected:
136 TC_LOG_MMAP_no_msync* tc_log_mmap;
137 Server_initializer initializer;
138 };
139
140 namespace tc_log_mmap_unittest {
141
TEST_F(TCLogMMapTest,TClogCommit)142 TEST_F(TCLogMMapTest, TClogCommit)
143 {
144 // test calling of log/unlog for xid=1
145 testCommit(1);
146 }
147
148 class TC_Log_MMap_thread : public thread::Thread
149 {
150 public:
TC_Log_MMap_thread()151 TC_Log_MMap_thread()
152 : m_start_xid(0), m_end_xid(0),
153 m_tc_log_mmap(NULL), initializer(NULL)
154 {
155 }
156
~TC_Log_MMap_thread()157 ~TC_Log_MMap_thread()
158 {
159 initializer->TearDown();
160 delete initializer;
161 }
162
init(ulonglong start_value,ulonglong end_value,TCLogMMapTest * tc_log_mmap,Server_initializer * initializer_value)163 void init (ulonglong start_value, ulonglong end_value,
164 TCLogMMapTest* tc_log_mmap, Server_initializer* initializer_value)
165 {
166 m_start_xid= start_value;
167 m_end_xid= end_value;
168 m_tc_log_mmap= tc_log_mmap;
169 initializer= initializer_value;
170 initializer->SetUp();
171 }
172
run()173 virtual void run()
174 {
175 ulonglong xid= m_start_xid;
176 while (xid < m_end_xid)
177 {
178 m_tc_log_mmap->testCommit(xid++, initializer->thd());
179 }
180 }
181
182 protected:
183 ulonglong m_start_xid, m_end_xid;
184 TCLogMMapTest* m_tc_log_mmap;
185 Server_initializer* initializer;
186 };
187
TEST_F(TCLogMMapTest,ConcurrentAccess)188 TEST_F(TCLogMMapTest, ConcurrentAccess)
189 {
190 static const unsigned MAX_WORKER_THREADS= 10;
191 static const unsigned VALUE_INTERVAL= 100;
192
193 TC_Log_MMap_thread tclog_threads[MAX_WORKER_THREADS];
194
195 ulonglong start_value= 0;
196 for (unsigned i=0; i < MAX_WORKER_THREADS; ++i)
197 {
198 /*
199 Each thread gets a dedicated instance of class Server_initializer and
200 hence it also gets a separate THD object.
201 */
202 tclog_threads[i].init(start_value, start_value + VALUE_INTERVAL, this,
203 new Server_initializer());
204 tclog_threads[i].start();
205 start_value+= VALUE_INTERVAL;
206 }
207
208 for (unsigned i=0; i < MAX_WORKER_THREADS; ++i)
209 tclog_threads[i].join();
210 }
211
212
TEST_F(TCLogMMapTest,FillAllPagesAndReuse)213 TEST_F(TCLogMMapTest, FillAllPagesAndReuse)
214 {
215 /* Get maximum number of XIDs which can be stored in TC log. */
216 const uint MAX_XIDS= tc_log_mmap->size();
217 ulong cookie;
218 /* Fill TC log. */
219 for(my_xid xid= 1; xid < MAX_XIDS; ++xid)
220 (void)testLog(xid);
221 cookie= testLog(MAX_XIDS);
222 /*
223 Now free one slot and try to reuse it.
224 This should work and not crash on assert.
225 */
226 testUnlog(cookie, MAX_XIDS);
227 testLog(MAX_XIDS + 1);
228 }
229
230
TEST_F(TCLogMMapTest,ConcurrentOverflow)231 TEST_F(TCLogMMapTest, ConcurrentOverflow)
232 {
233 const uint WORKER_THREADS= 10;
234 const uint XIDS_TO_REUSE= 100;
235
236 /*
237 Get maximum number of XIDs which can be stored in TC log.
238 */
239 const uint MAX_XIDS= tc_log_mmap->size();
240 ulong cookies[XIDS_TO_REUSE];
241
242 /*
243 Fill TC log. Remember cookies for last XIDS_TO_REUSE xids.
244 */
245 for(my_xid xid= 1; xid <= MAX_XIDS - XIDS_TO_REUSE; ++xid)
246 testLog(xid);
247 for (uint i= 0; i < XIDS_TO_REUSE; ++i)
248 cookies[i]= testLog(MAX_XIDS - XIDS_TO_REUSE + 1 + i);
249
250 /*
251 Now create several threads which will try to do commit.
252 Since log is full they will have to wait until we free some slots.
253 */
254 TC_Log_MMap_thread threads[WORKER_THREADS];
255 for (uint i= 0; i < WORKER_THREADS; ++i)
256 {
257 /*
258 Each thread gets a dedicated instance of class Server_initializer and hence
259 it also gets a separate THD object.
260 */
261 threads[i].init(MAX_XIDS + i * (XIDS_TO_REUSE/WORKER_THREADS),
262 MAX_XIDS + (i + 1) * (XIDS_TO_REUSE/WORKER_THREADS), this,
263 new Server_initializer());
264 threads[i].start();
265 }
266
267 /*
268 Once started all threads should block since we are out of free slots
269 in the log, Resume threads by freeing necessary slots. Resumed thread
270 should not hang or assert.
271 */
272 for (uint i= 0; i < XIDS_TO_REUSE; ++i)
273 testUnlog(cookies[i], MAX_XIDS - XIDS_TO_REUSE + 1 + i);
274
275 /* Wait till all threads are done. */
276 for (uint i=0; i < WORKER_THREADS; ++i)
277 threads[i].join();
278 }
279
280 }
281