1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, 2018, MariaDB Corporation. |
5 | |
6 | Portions of this file contain modifications contributed and copyrighted by |
7 | Google, Inc. Those modifications are gratefully acknowledged and are described |
8 | briefly in the InnoDB documentation. The contributions by Google are |
9 | incorporated with their permission, and subject to the conditions contained in |
10 | the file COPYING.Google. |
11 | |
12 | This program is free software; you can redistribute it and/or modify it under |
13 | the terms of the GNU General Public License as published by the Free Software |
14 | Foundation; version 2 of the License. |
15 | |
16 | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU General Public License along with |
21 | this program; if not, write to the Free Software Foundation, Inc., |
22 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
23 | |
24 | *****************************************************************************/ |
25 | |
26 | /**************************************************//** |
27 | @file sync/sync0debug.cc |
28 | Debug checks for latches. |
29 | |
30 | Created 2012-08-21 Sunny Bains |
31 | *******************************************************/ |
32 | |
33 | #include "sync0sync.h" |
34 | #include "sync0debug.h" |
35 | |
36 | #include "ut0new.h" |
37 | #include "srv0start.h" |
38 | |
39 | #include <map> |
40 | #include <vector> |
41 | #include <string> |
42 | #include <algorithm> |
43 | #include <iostream> |
44 | |
45 | #ifdef UNIV_DEBUG |
46 | |
47 | my_bool srv_sync_debug; |
48 | |
49 | /** The global mutex which protects debug info lists of all rw-locks. |
50 | To modify the debug info list of an rw-lock, this mutex has to be |
51 | acquired in addition to the mutex protecting the lock. */ |
52 | static SysMutex rw_lock_debug_mutex; |
53 | |
54 | /** The latch held by a thread */ |
55 | struct Latched { |
56 | |
57 | /** Constructor */ |
58 | Latched() : m_latch(), m_level(SYNC_UNKNOWN) { } |
59 | |
60 | /** Constructor |
61 | @param[in] latch Latch instance |
62 | @param[in] level Level of latch held */ |
63 | Latched(const latch_t* latch, |
64 | latch_level_t level) |
65 | : |
66 | m_latch(latch), |
67 | m_level(level) |
68 | { |
69 | /* No op */ |
70 | } |
71 | |
72 | /** @return the latch level */ |
73 | latch_level_t get_level() const |
74 | { |
75 | return(m_level); |
76 | } |
77 | |
78 | /** Check if the rhs latch and level match |
79 | @param[in] rhs instance to compare with |
80 | @return true on match */ |
81 | bool operator==(const Latched& rhs) const |
82 | { |
83 | return(m_latch == rhs.m_latch && m_level == rhs.m_level); |
84 | } |
85 | |
86 | /** The latch instance */ |
87 | const latch_t* m_latch; |
88 | |
89 | /** The latch level. For buffer blocks we can pass a separate latch |
90 | level to check against, see buf_block_dbg_add_level() */ |
91 | latch_level_t m_level; |
92 | }; |
93 | |
94 | /** Thread specific latches. This is ordered on level in descending order. */ |
95 | typedef std::vector<Latched, ut_allocator<Latched> > Latches; |
96 | |
97 | /** The deadlock detector. */ |
98 | struct LatchDebug { |
99 | |
100 | /** Debug mutex for control structures, should not be tracked |
101 | by this module. */ |
102 | typedef OSMutex Mutex; |
103 | |
104 | /** Comparator for the ThreadMap. */ |
105 | struct os_thread_id_less |
106 | : public std::binary_function< |
107 | os_thread_id_t, |
108 | os_thread_id_t, |
109 | bool> |
110 | { |
111 | /** @return true if lhs < rhs */ |
112 | bool operator()( |
113 | const os_thread_id_t& lhs, |
114 | const os_thread_id_t& rhs) const |
115 | UNIV_NOTHROW |
116 | { |
117 | return(os_thread_pf(lhs) < os_thread_pf(rhs)); |
118 | } |
119 | }; |
120 | |
121 | /** For tracking a thread's latches. */ |
122 | typedef std::map< |
123 | os_thread_id_t, |
124 | Latches*, |
125 | os_thread_id_less, |
126 | ut_allocator<std::pair<const os_thread_id_t, Latches*> > > |
127 | ThreadMap; |
128 | |
129 | /** Constructor */ |
130 | LatchDebug() |
131 | UNIV_NOTHROW; |
132 | |
133 | /** Destructor */ |
134 | ~LatchDebug() |
135 | UNIV_NOTHROW |
136 | { |
137 | m_mutex.destroy(); |
138 | } |
139 | |
140 | /** Create a new instance if one doesn't exist else return |
141 | the existing one. |
142 | @param[in] add add an empty entry if one is not |
143 | found (default no) |
144 | @return pointer to a thread's acquired latches. */ |
145 | Latches* thread_latches(bool add = false) |
146 | UNIV_NOTHROW; |
147 | |
148 | /** Check that all the latches already owned by a thread have a lower |
149 | level than limit. |
150 | @param[in] latches the thread's existing (acquired) latches |
151 | @param[in] limit to check against |
152 | @return latched if there is one with a level <= limit . */ |
153 | const Latched* less( |
154 | const Latches* latches, |
155 | latch_level_t limit) const |
156 | UNIV_NOTHROW; |
157 | |
158 | /** Checks if the level value exists in the thread's acquired latches. |
159 | @param[in] latches the thread's existing (acquired) latches |
160 | @param[in] level to lookup |
161 | @return latch if found or 0 */ |
162 | const latch_t* find( |
163 | const Latches* Latches, |
164 | latch_level_t level) const |
165 | UNIV_NOTHROW; |
166 | |
167 | /** |
168 | Checks if the level value exists in the thread's acquired latches. |
169 | @param[in] level to lookup |
170 | @return latch if found or 0 */ |
171 | const latch_t* find(latch_level_t level) |
172 | UNIV_NOTHROW; |
173 | |
174 | /** Report error and abort. |
175 | @param[in] latches thread's existing latches |
176 | @param[in] latched The existing latch causing the |
177 | invariant to fail |
178 | @param[in] level The new level request that breaks |
179 | the order */ |
180 | void crash( |
181 | const Latches* latches, |
182 | const Latched* latched, |
183 | latch_level_t level) const |
184 | UNIV_NOTHROW; |
185 | |
186 | /** Do a basic ordering check. |
187 | @param[in] latches thread's existing latches |
188 | @param[in] requested_level Level requested by latch |
189 | @param[in] level declared ulint so that we can |
190 | do level - 1. The level of the |
191 | latch that the thread is trying |
192 | to acquire |
193 | @return true if passes, else crash with error message. */ |
194 | inline bool basic_check( |
195 | const Latches* latches, |
196 | latch_level_t requested_level, |
197 | lint level) const |
198 | UNIV_NOTHROW; |
199 | |
200 | /** Adds a latch and its level in the thread level array. Allocates |
201 | the memory for the array if called for the first time for this |
202 | OS thread. Makes the checks against other latch levels stored |
203 | in the array for this thread. |
204 | |
205 | @param[in] latch latch that the thread wants to acqire. |
206 | @param[in] level latch level to check against */ |
207 | void lock_validate( |
208 | const latch_t* latch, |
209 | latch_level_t level) |
210 | UNIV_NOTHROW |
211 | { |
212 | /* Ignore diagnostic latches, starting with '.' */ |
213 | |
214 | if (*latch->get_name() != '.' |
215 | && latch->get_level() != SYNC_LEVEL_VARYING) { |
216 | |
217 | ut_ad(level != SYNC_LEVEL_VARYING); |
218 | |
219 | Latches* latches = check_order(latch, level); |
220 | |
221 | ut_a(latches->empty() |
222 | || level == SYNC_LEVEL_VARYING |
223 | || level == SYNC_NO_ORDER_CHECK |
224 | || latches->back().get_level() |
225 | == SYNC_NO_ORDER_CHECK |
226 | || latches->back().m_latch->get_level() |
227 | == SYNC_LEVEL_VARYING |
228 | || latches->back().get_level() >= level); |
229 | } |
230 | } |
231 | |
232 | /** Adds a latch and its level in the thread level array. Allocates |
233 | the memory for the array if called for the first time for this |
234 | OS thread. Makes the checks against other latch levels stored |
235 | in the array for this thread. |
236 | |
237 | @param[in] latch latch that the thread wants to acqire. |
238 | @param[in] level latch level to check against */ |
239 | void lock_granted( |
240 | const latch_t* latch, |
241 | latch_level_t level) |
242 | UNIV_NOTHROW |
243 | { |
244 | /* Ignore diagnostic latches, starting with '.' */ |
245 | |
246 | if (*latch->get_name() != '.' |
247 | && latch->get_level() != SYNC_LEVEL_VARYING) { |
248 | |
249 | Latches* latches = thread_latches(true); |
250 | |
251 | latches->push_back(Latched(latch, level)); |
252 | } |
253 | } |
254 | |
255 | /** For recursive X rw-locks. |
256 | @param[in] latch The RW-Lock to relock */ |
257 | void relock(const latch_t* latch) |
258 | UNIV_NOTHROW |
259 | { |
260 | ut_a(latch->m_rw_lock); |
261 | |
262 | latch_level_t level = latch->get_level(); |
263 | |
264 | /* Ignore diagnostic latches, starting with '.' */ |
265 | |
266 | if (*latch->get_name() != '.' |
267 | && latch->get_level() != SYNC_LEVEL_VARYING) { |
268 | |
269 | Latches* latches = thread_latches(true); |
270 | |
271 | Latches::iterator it = std::find( |
272 | latches->begin(), latches->end(), |
273 | Latched(latch, level)); |
274 | |
275 | ut_a(latches->empty() |
276 | || level == SYNC_LEVEL_VARYING |
277 | || level == SYNC_NO_ORDER_CHECK |
278 | || latches->back().m_latch->get_level() |
279 | == SYNC_LEVEL_VARYING |
280 | || latches->back().m_latch->get_level() |
281 | == SYNC_NO_ORDER_CHECK |
282 | || latches->back().get_level() >= level |
283 | || it != latches->end()); |
284 | |
285 | if (it == latches->end()) { |
286 | latches->push_back(Latched(latch, level)); |
287 | } else { |
288 | latches->insert(it, Latched(latch, level)); |
289 | } |
290 | } |
291 | } |
292 | |
293 | /** Iterate over a thread's latches. |
294 | @param[in] functor The callback |
295 | @return true if the functor returns true. */ |
296 | bool for_each(const sync_check_functor_t& functor) |
297 | UNIV_NOTHROW |
298 | { |
299 | if (const Latches* latches = thread_latches()) { |
300 | Latches::const_iterator end = latches->end(); |
301 | for (Latches::const_iterator it = latches->begin(); |
302 | it != end; ++it) { |
303 | |
304 | if (functor(it->m_level)) { |
305 | return(true); |
306 | } |
307 | } |
308 | } |
309 | |
310 | return(false); |
311 | } |
312 | |
313 | /** Removes a latch from the thread level array if it is found there. |
314 | @param[in] latch The latch that was released |
315 | @return true if found in the array; it is not an error if the latch is |
316 | not found, as we presently are not able to determine the level for |
317 | every latch reservation the program does */ |
318 | void unlock(const latch_t* latch) UNIV_NOTHROW; |
319 | |
320 | /** Get the level name |
321 | @param[in] level The level ID to lookup |
322 | @return level name */ |
323 | const std::string& get_level_name(latch_level_t level) const |
324 | UNIV_NOTHROW |
325 | { |
326 | Levels::const_iterator it = m_levels.find(level); |
327 | |
328 | ut_ad(it != m_levels.end()); |
329 | |
330 | return(it->second); |
331 | } |
332 | |
333 | /** Initialise the debug data structures */ |
334 | static void init() |
335 | UNIV_NOTHROW; |
336 | |
337 | /** Shutdown the latch debug checking */ |
338 | static void shutdown() |
339 | UNIV_NOTHROW; |
340 | |
341 | /** @return the singleton instance */ |
342 | static LatchDebug* instance() |
343 | UNIV_NOTHROW |
344 | { |
345 | return(s_instance); |
346 | } |
347 | |
348 | /** Create the singleton instance */ |
349 | static void create_instance() |
350 | UNIV_NOTHROW |
351 | { |
352 | ut_ad(s_instance == NULL); |
353 | |
354 | s_instance = UT_NEW_NOKEY(LatchDebug()); |
355 | } |
356 | |
357 | private: |
358 | /** Disable copying */ |
359 | LatchDebug(const LatchDebug&); |
360 | LatchDebug& operator=(const LatchDebug&); |
361 | |
362 | /** Adds a latch and its level in the thread level array. Allocates |
363 | the memory for the array if called first time for this OS thread. |
364 | Makes the checks against other latch levels stored in the array |
365 | for this thread. |
366 | |
367 | @param[in] latch pointer to a mutex or an rw-lock |
368 | @param[in] level level in the latching order |
369 | @return the thread's latches */ |
370 | Latches* check_order( |
371 | const latch_t* latch, |
372 | latch_level_t level) |
373 | UNIV_NOTHROW; |
374 | |
375 | /** Print the latches acquired by a thread |
376 | @param[in] latches Latches acquired by a thread */ |
377 | void print_latches(const Latches* latches) const |
378 | UNIV_NOTHROW; |
379 | |
380 | /** Special handling for the RTR mutexes. We need to add proper |
381 | levels for them if possible. |
382 | @param[in] latch Latch to check |
383 | @return true if it is a an _RTR_ mutex */ |
384 | bool is_rtr_mutex(const latch_t* latch) const |
385 | UNIV_NOTHROW |
386 | { |
387 | return(latch->get_id() == LATCH_ID_RTR_ACTIVE_MUTEX |
388 | || latch->get_id() == LATCH_ID_RTR_PATH_MUTEX |
389 | || latch->get_id() == LATCH_ID_RTR_MATCH_MUTEX |
390 | || latch->get_id() == LATCH_ID_RTR_SSN_MUTEX); |
391 | } |
392 | |
393 | private: |
394 | /** Comparator for the Levels . */ |
395 | struct latch_level_less |
396 | : public std::binary_function< |
397 | latch_level_t, |
398 | latch_level_t, |
399 | bool> |
400 | { |
401 | /** @return true if lhs < rhs */ |
402 | bool operator()( |
403 | const latch_level_t& lhs, |
404 | const latch_level_t& rhs) const |
405 | UNIV_NOTHROW |
406 | { |
407 | return(lhs < rhs); |
408 | } |
409 | }; |
410 | |
411 | typedef std::map< |
412 | latch_level_t, |
413 | std::string, |
414 | latch_level_less, |
415 | ut_allocator<std::pair<const latch_level_t, std::string> > > |
416 | Levels; |
417 | |
418 | /** Mutex protecting the deadlock detector data structures. */ |
419 | Mutex m_mutex; |
420 | |
421 | /** Thread specific data. Protected by m_mutex. */ |
422 | ThreadMap m_threads; |
423 | |
424 | /** Mapping from latche level to its string representation. */ |
425 | Levels m_levels; |
426 | |
427 | /** The singleton instance. Must be created in single threaded mode. */ |
428 | static LatchDebug* s_instance; |
429 | |
430 | public: |
431 | /** For checking whether this module has been initialised or not. */ |
432 | static bool s_initialized; |
433 | }; |
434 | |
435 | /** The latch order checking infra-structure */ |
436 | LatchDebug* LatchDebug::s_instance = NULL; |
437 | bool LatchDebug::s_initialized = false; |
438 | |
439 | #define LEVEL_MAP_INSERT(T) \ |
440 | do { \ |
441 | std::pair<Levels::iterator, bool> result = \ |
442 | m_levels.insert(Levels::value_type(T, #T)); \ |
443 | ut_ad(result.second); \ |
444 | } while(0) |
445 | |
446 | /** Setup the mapping from level ID to level name mapping */ |
447 | LatchDebug::LatchDebug() |
448 | { |
449 | m_mutex.init(); |
450 | |
451 | LEVEL_MAP_INSERT(SYNC_UNKNOWN); |
452 | LEVEL_MAP_INSERT(SYNC_MUTEX); |
453 | LEVEL_MAP_INSERT(RW_LOCK_SX); |
454 | LEVEL_MAP_INSERT(RW_LOCK_X_WAIT); |
455 | LEVEL_MAP_INSERT(RW_LOCK_S); |
456 | LEVEL_MAP_INSERT(RW_LOCK_X); |
457 | LEVEL_MAP_INSERT(RW_LOCK_NOT_LOCKED); |
458 | LEVEL_MAP_INSERT(SYNC_MONITOR_MUTEX); |
459 | LEVEL_MAP_INSERT(SYNC_ANY_LATCH); |
460 | LEVEL_MAP_INSERT(SYNC_DOUBLEWRITE); |
461 | LEVEL_MAP_INSERT(SYNC_BUF_FLUSH_LIST); |
462 | LEVEL_MAP_INSERT(SYNC_BUF_BLOCK); |
463 | LEVEL_MAP_INSERT(SYNC_BUF_PAGE_HASH); |
464 | LEVEL_MAP_INSERT(SYNC_BUF_POOL); |
465 | LEVEL_MAP_INSERT(SYNC_POOL); |
466 | LEVEL_MAP_INSERT(SYNC_POOL_MANAGER); |
467 | LEVEL_MAP_INSERT(SYNC_SEARCH_SYS); |
468 | LEVEL_MAP_INSERT(SYNC_WORK_QUEUE); |
469 | LEVEL_MAP_INSERT(SYNC_FTS_TOKENIZE); |
470 | LEVEL_MAP_INSERT(SYNC_FTS_OPTIMIZE); |
471 | LEVEL_MAP_INSERT(SYNC_FTS_BG_THREADS); |
472 | LEVEL_MAP_INSERT(SYNC_FTS_CACHE_INIT); |
473 | LEVEL_MAP_INSERT(SYNC_RECV); |
474 | LEVEL_MAP_INSERT(SYNC_LOG_FLUSH_ORDER); |
475 | LEVEL_MAP_INSERT(SYNC_LOG); |
476 | LEVEL_MAP_INSERT(SYNC_LOG_WRITE); |
477 | LEVEL_MAP_INSERT(SYNC_PAGE_CLEANER); |
478 | LEVEL_MAP_INSERT(SYNC_PURGE_QUEUE); |
479 | LEVEL_MAP_INSERT(SYNC_TRX_SYS_HEADER); |
480 | LEVEL_MAP_INSERT(SYNC_REC_LOCK); |
481 | LEVEL_MAP_INSERT(SYNC_THREADS); |
482 | LEVEL_MAP_INSERT(SYNC_TRX); |
483 | LEVEL_MAP_INSERT(SYNC_RW_TRX_HASH_ELEMENT); |
484 | LEVEL_MAP_INSERT(SYNC_TRX_SYS); |
485 | LEVEL_MAP_INSERT(SYNC_LOCK_SYS); |
486 | LEVEL_MAP_INSERT(SYNC_LOCK_WAIT_SYS); |
487 | LEVEL_MAP_INSERT(SYNC_INDEX_ONLINE_LOG); |
488 | LEVEL_MAP_INSERT(SYNC_IBUF_BITMAP); |
489 | LEVEL_MAP_INSERT(SYNC_IBUF_BITMAP_MUTEX); |
490 | LEVEL_MAP_INSERT(SYNC_IBUF_TREE_NODE); |
491 | LEVEL_MAP_INSERT(SYNC_IBUF_TREE_NODE_NEW); |
492 | LEVEL_MAP_INSERT(SYNC_IBUF_INDEX_TREE); |
493 | LEVEL_MAP_INSERT(SYNC_IBUF_MUTEX); |
494 | LEVEL_MAP_INSERT(SYNC_FSP_PAGE); |
495 | LEVEL_MAP_INSERT(SYNC_FSP); |
496 | LEVEL_MAP_INSERT(SYNC_EXTERN_STORAGE); |
497 | LEVEL_MAP_INSERT(SYNC_TRX_UNDO_PAGE); |
498 | LEVEL_MAP_INSERT(SYNC_RSEG_HEADER); |
499 | LEVEL_MAP_INSERT(SYNC_RSEG_HEADER_NEW); |
500 | LEVEL_MAP_INSERT(SYNC_NOREDO_RSEG); |
501 | LEVEL_MAP_INSERT(SYNC_REDO_RSEG); |
502 | LEVEL_MAP_INSERT(SYNC_PURGE_LATCH); |
503 | LEVEL_MAP_INSERT(SYNC_TREE_NODE); |
504 | LEVEL_MAP_INSERT(SYNC_TREE_NODE_FROM_HASH); |
505 | LEVEL_MAP_INSERT(SYNC_TREE_NODE_NEW); |
506 | LEVEL_MAP_INSERT(SYNC_INDEX_TREE); |
507 | LEVEL_MAP_INSERT(SYNC_IBUF_PESS_INSERT_MUTEX); |
508 | LEVEL_MAP_INSERT(SYNC_IBUF_HEADER); |
509 | LEVEL_MAP_INSERT(SYNC_DICT_HEADER); |
510 | LEVEL_MAP_INSERT(SYNC_STATS_AUTO_RECALC); |
511 | LEVEL_MAP_INSERT(SYNC_DICT_AUTOINC_MUTEX); |
512 | LEVEL_MAP_INSERT(SYNC_DICT); |
513 | LEVEL_MAP_INSERT(SYNC_FTS_CACHE); |
514 | LEVEL_MAP_INSERT(SYNC_DICT_OPERATION); |
515 | LEVEL_MAP_INSERT(SYNC_TRX_I_S_LAST_READ); |
516 | LEVEL_MAP_INSERT(SYNC_TRX_I_S_RWLOCK); |
517 | LEVEL_MAP_INSERT(SYNC_RECV_WRITER); |
518 | LEVEL_MAP_INSERT(SYNC_LEVEL_VARYING); |
519 | LEVEL_MAP_INSERT(SYNC_NO_ORDER_CHECK); |
520 | |
521 | /* Enum count starts from 0 */ |
522 | ut_ad(m_levels.size() == SYNC_LEVEL_MAX + 1); |
523 | } |
524 | |
525 | /** Print the latches acquired by a thread |
526 | @param[in] latches Latches acquired by a thread */ |
527 | void |
528 | LatchDebug::print_latches(const Latches* latches) const |
529 | UNIV_NOTHROW |
530 | { |
531 | ib::error() << "Latches already owned by this thread: " ; |
532 | |
533 | Latches::const_iterator end = latches->end(); |
534 | |
535 | for (Latches::const_iterator it = latches->begin(); |
536 | it != end; |
537 | ++it) { |
538 | |
539 | ib::error() |
540 | << sync_latch_get_name(it->m_latch->get_id()) |
541 | << " -> " |
542 | << it->m_level << " " |
543 | << "(" << get_level_name(it->m_level) << ")" ; |
544 | } |
545 | } |
546 | |
547 | /** Report error and abort |
548 | @param[in] latches thread's existing latches |
549 | @param[in] latched The existing latch causing the invariant to fail |
550 | @param[in] level The new level request that breaks the order */ |
551 | void |
552 | LatchDebug::crash( |
553 | const Latches* latches, |
554 | const Latched* latched, |
555 | latch_level_t level) const |
556 | UNIV_NOTHROW |
557 | { |
558 | const latch_t* latch = latched->m_latch; |
559 | const std::string& in_level_name = get_level_name(level); |
560 | |
561 | const std::string& latch_level_name = |
562 | get_level_name(latched->m_level); |
563 | |
564 | ib::error() |
565 | << "Thread " << os_thread_pf(os_thread_get_curr_id()) |
566 | << " already owns a latch " |
567 | << sync_latch_get_name(latch->m_id) << " at level" |
568 | << " " << latched->m_level << " (" << latch_level_name |
569 | << " ), which is at a lower/same level than the" |
570 | << " requested latch: " |
571 | << level << " (" << in_level_name << "). " |
572 | << latch->to_string(); |
573 | |
574 | print_latches(latches); |
575 | |
576 | ut_error; |
577 | } |
578 | |
579 | /** Check that all the latches already owned by a thread have a lower |
580 | level than limit. |
581 | @param[in] latches the thread's existing (acquired) latches |
582 | @param[in] limit to check against |
583 | @return latched info if there is one with a level <= limit . */ |
584 | const Latched* |
585 | LatchDebug::less( |
586 | const Latches* latches, |
587 | latch_level_t limit) const |
588 | UNIV_NOTHROW |
589 | { |
590 | Latches::const_iterator end = latches->end(); |
591 | |
592 | for (Latches::const_iterator it = latches->begin(); it != end; ++it) { |
593 | |
594 | if (it->m_level <= limit) { |
595 | return(&(*it)); |
596 | } |
597 | } |
598 | |
599 | return(NULL); |
600 | } |
601 | |
602 | /** Do a basic ordering check. |
603 | @param[in] latches thread's existing latches |
604 | @param[in] requested_level Level requested by latch |
605 | @param[in] in_level declared ulint so that we can do level - 1. |
606 | The level of the latch that the thread is |
607 | trying to acquire |
608 | @return true if passes, else crash with error message. */ |
609 | inline bool |
610 | LatchDebug::basic_check( |
611 | const Latches* latches, |
612 | latch_level_t requested_level, |
613 | lint in_level) const |
614 | UNIV_NOTHROW |
615 | { |
616 | latch_level_t level = latch_level_t(in_level); |
617 | |
618 | ut_ad(level < SYNC_LEVEL_MAX); |
619 | |
620 | const Latched* latched = less(latches, level); |
621 | |
622 | if (latched != NULL) { |
623 | crash(latches, latched, requested_level); |
624 | return(false); |
625 | } |
626 | |
627 | return(true); |
628 | } |
629 | |
630 | /** Create a new instance if one doesn't exist else return the existing one. |
631 | @param[in] add add an empty entry if one is not found |
632 | (default no) |
633 | @return pointer to a thread's acquired latches. */ |
634 | Latches* |
635 | LatchDebug::thread_latches(bool add) |
636 | UNIV_NOTHROW |
637 | { |
638 | m_mutex.enter(); |
639 | |
640 | os_thread_id_t thread_id = os_thread_get_curr_id(); |
641 | ThreadMap::iterator lb = m_threads.lower_bound(thread_id); |
642 | |
643 | if (lb != m_threads.end() |
644 | && !(m_threads.key_comp()(thread_id, lb->first))) { |
645 | |
646 | Latches* latches = lb->second; |
647 | |
648 | m_mutex.exit(); |
649 | |
650 | return(latches); |
651 | |
652 | } else if (!add) { |
653 | |
654 | m_mutex.exit(); |
655 | |
656 | return(NULL); |
657 | |
658 | } else { |
659 | typedef ThreadMap::value_type value_type; |
660 | |
661 | Latches* latches = UT_NEW_NOKEY(Latches()); |
662 | |
663 | ut_a(latches != NULL); |
664 | |
665 | latches->reserve(32); |
666 | |
667 | m_threads.insert(lb, value_type(thread_id, latches)); |
668 | |
669 | m_mutex.exit(); |
670 | |
671 | return(latches); |
672 | } |
673 | } |
674 | |
675 | /** Checks if the level value exists in the thread's acquired latches. |
676 | @param[in] levels the thread's existing (acquired) latches |
677 | @param[in] level to lookup |
678 | @return latch if found or 0 */ |
679 | const latch_t* |
680 | LatchDebug::find( |
681 | const Latches* latches, |
682 | latch_level_t level) const UNIV_NOTHROW |
683 | { |
684 | Latches::const_iterator end = latches->end(); |
685 | |
686 | for (Latches::const_iterator it = latches->begin(); it != end; ++it) { |
687 | |
688 | if (it->m_level == level) { |
689 | |
690 | return(it->m_latch); |
691 | } |
692 | } |
693 | |
694 | return(0); |
695 | } |
696 | |
697 | /** Checks if the level value exists in the thread's acquired latches. |
698 | @param[in] level The level to lookup |
699 | @return latch if found or NULL */ |
700 | const latch_t* |
701 | LatchDebug::find(latch_level_t level) |
702 | UNIV_NOTHROW |
703 | { |
704 | return(find(thread_latches(), level)); |
705 | } |
706 | |
707 | /** |
708 | Adds a latch and its level in the thread level array. Allocates the memory |
709 | for the array if called first time for this OS thread. Makes the checks |
710 | against other latch levels stored in the array for this thread. |
711 | @param[in] latch pointer to a mutex or an rw-lock |
712 | @param[in] level level in the latching order |
713 | @return the thread's latches */ |
714 | Latches* |
715 | LatchDebug::check_order( |
716 | const latch_t* latch, |
717 | latch_level_t level) |
718 | UNIV_NOTHROW |
719 | { |
720 | ut_ad(latch->get_level() != SYNC_LEVEL_VARYING); |
721 | |
722 | Latches* latches = thread_latches(true); |
723 | |
724 | /* NOTE that there is a problem with _NODE and _LEAF levels: if the |
725 | B-tree height changes, then a leaf can change to an internal node |
726 | or the other way around. We do not know at present if this can cause |
727 | unnecessary assertion failures below. */ |
728 | |
729 | switch (level) { |
730 | case SYNC_NO_ORDER_CHECK: |
731 | case SYNC_EXTERN_STORAGE: |
732 | case SYNC_TREE_NODE_FROM_HASH: |
733 | /* Do no order checking */ |
734 | break; |
735 | |
736 | case SYNC_TRX_SYS_HEADER: |
737 | |
738 | if (srv_is_being_started) { |
739 | /* This is violated during trx_sys_create_rsegs() |
740 | when creating additional rollback segments when |
741 | upgrading in srv_start(). */ |
742 | break; |
743 | } |
744 | |
745 | /* Fall through */ |
746 | |
747 | case SYNC_MONITOR_MUTEX: |
748 | case SYNC_RECV: |
749 | case SYNC_FTS_BG_THREADS: |
750 | case SYNC_WORK_QUEUE: |
751 | case SYNC_FTS_TOKENIZE: |
752 | case SYNC_FTS_OPTIMIZE: |
753 | case SYNC_FTS_CACHE: |
754 | case SYNC_FTS_CACHE_INIT: |
755 | case SYNC_PAGE_CLEANER: |
756 | case SYNC_LOG: |
757 | case SYNC_LOG_WRITE: |
758 | case SYNC_LOG_FLUSH_ORDER: |
759 | case SYNC_DOUBLEWRITE: |
760 | case SYNC_SEARCH_SYS: |
761 | case SYNC_THREADS: |
762 | case SYNC_LOCK_SYS: |
763 | case SYNC_LOCK_WAIT_SYS: |
764 | case SYNC_RW_TRX_HASH_ELEMENT: |
765 | case SYNC_TRX_SYS: |
766 | case SYNC_IBUF_BITMAP_MUTEX: |
767 | case SYNC_REDO_RSEG: |
768 | case SYNC_NOREDO_RSEG: |
769 | case SYNC_PURGE_LATCH: |
770 | case SYNC_PURGE_QUEUE: |
771 | case SYNC_DICT_AUTOINC_MUTEX: |
772 | case SYNC_DICT_OPERATION: |
773 | case SYNC_DICT_HEADER: |
774 | case SYNC_TRX_I_S_RWLOCK: |
775 | case SYNC_TRX_I_S_LAST_READ: |
776 | case SYNC_IBUF_MUTEX: |
777 | case SYNC_INDEX_ONLINE_LOG: |
778 | case SYNC_STATS_AUTO_RECALC: |
779 | case SYNC_POOL: |
780 | case SYNC_POOL_MANAGER: |
781 | case SYNC_RECV_WRITER: |
782 | |
783 | basic_check(latches, level, level); |
784 | break; |
785 | |
786 | case SYNC_ANY_LATCH: |
787 | |
788 | /* Temporary workaround for LATCH_ID_RTR_*_MUTEX */ |
789 | if (is_rtr_mutex(latch)) { |
790 | |
791 | const Latched* latched = less(latches, level); |
792 | |
793 | if (latched == NULL |
794 | || (latched != NULL |
795 | && is_rtr_mutex(latched->m_latch))) { |
796 | |
797 | /* No violation */ |
798 | break; |
799 | |
800 | } |
801 | |
802 | crash(latches, latched, level); |
803 | |
804 | } else { |
805 | basic_check(latches, level, level); |
806 | } |
807 | |
808 | break; |
809 | |
810 | case SYNC_TRX: |
811 | |
812 | /* Either the thread must own the lock_sys.mutex, or |
813 | it is allowed to own only ONE trx_t::mutex. */ |
814 | |
815 | if (less(latches, level) != NULL) { |
816 | basic_check(latches, level, level - 1); |
817 | ut_a(find(latches, SYNC_LOCK_SYS) != 0); |
818 | } |
819 | break; |
820 | |
821 | case SYNC_BUF_FLUSH_LIST: |
822 | case SYNC_BUF_POOL: |
823 | |
824 | /* We can have multiple mutexes of this type therefore we |
825 | can only check whether the greater than condition holds. */ |
826 | |
827 | basic_check(latches, level, level - 1); |
828 | break; |
829 | |
830 | case SYNC_BUF_PAGE_HASH: |
831 | |
832 | /* Multiple page_hash locks are only allowed during |
833 | buf_validate and that is where buf_pool mutex is already |
834 | held. */ |
835 | |
836 | /* Fall through */ |
837 | |
838 | case SYNC_BUF_BLOCK: |
839 | |
840 | /* Either the thread must own the (buffer pool) buf_pool->mutex |
841 | or it is allowed to latch only ONE of (buffer block) |
842 | block->mutex or buf_pool->zip_mutex. */ |
843 | |
844 | if (less(latches, level) != NULL) { |
845 | basic_check(latches, level, level - 1); |
846 | ut_a(find(latches, SYNC_BUF_POOL) != 0); |
847 | } |
848 | break; |
849 | |
850 | case SYNC_REC_LOCK: |
851 | |
852 | if (find(latches, SYNC_LOCK_SYS) != 0) { |
853 | basic_check(latches, level, SYNC_REC_LOCK - 1); |
854 | } else { |
855 | basic_check(latches, level, SYNC_REC_LOCK); |
856 | } |
857 | break; |
858 | |
859 | case SYNC_IBUF_BITMAP: |
860 | |
861 | /* Either the thread must own the master mutex to all |
862 | the bitmap pages, or it is allowed to latch only ONE |
863 | bitmap page. */ |
864 | |
865 | if (find(latches, SYNC_IBUF_BITMAP_MUTEX) != 0) { |
866 | |
867 | basic_check(latches, level, SYNC_IBUF_BITMAP - 1); |
868 | |
869 | } else if (!srv_is_being_started) { |
870 | |
871 | /* This is violated during trx_sys_create_rsegs() |
872 | when creating additional rollback segments during |
873 | upgrade. */ |
874 | |
875 | basic_check(latches, level, SYNC_IBUF_BITMAP); |
876 | } |
877 | break; |
878 | |
879 | case SYNC_FSP_PAGE: |
880 | ut_a(find(latches, SYNC_FSP) != 0); |
881 | break; |
882 | |
883 | case SYNC_FSP: |
884 | |
885 | ut_a(find(latches, SYNC_FSP) != 0 |
886 | || basic_check(latches, level, SYNC_FSP)); |
887 | break; |
888 | |
889 | case SYNC_TRX_UNDO_PAGE: |
890 | |
891 | /* Purge is allowed to read in as many UNDO pages as it likes. |
892 | The purge thread can read the UNDO pages without any covering |
893 | mutex. */ |
894 | |
895 | ut_a(find(latches, SYNC_REDO_RSEG) != 0 |
896 | || find(latches, SYNC_NOREDO_RSEG) != 0 |
897 | || basic_check(latches, level, level - 1)); |
898 | break; |
899 | |
900 | case SYNC_RSEG_HEADER: |
901 | |
902 | ut_a(find(latches, SYNC_REDO_RSEG) != 0 |
903 | || find(latches, SYNC_NOREDO_RSEG) != 0); |
904 | break; |
905 | |
906 | case SYNC_RSEG_HEADER_NEW: |
907 | |
908 | ut_a(find(latches, SYNC_FSP_PAGE) != 0); |
909 | break; |
910 | |
911 | case SYNC_TREE_NODE: |
912 | |
913 | { |
914 | const latch_t* fsp_latch; |
915 | |
916 | fsp_latch = find(latches, SYNC_FSP); |
917 | |
918 | ut_a((fsp_latch != NULL |
919 | && fsp_latch->is_temp_fsp()) |
920 | || find(latches, SYNC_INDEX_TREE) != 0 |
921 | || find(latches, SYNC_DICT_OPERATION) |
922 | || basic_check(latches, |
923 | level, SYNC_TREE_NODE - 1)); |
924 | } |
925 | |
926 | break; |
927 | |
928 | case SYNC_TREE_NODE_NEW: |
929 | |
930 | ut_a(find(latches, SYNC_FSP_PAGE) != 0); |
931 | break; |
932 | |
933 | case SYNC_INDEX_TREE: |
934 | |
935 | basic_check(latches, level, SYNC_TREE_NODE - 1); |
936 | break; |
937 | |
938 | case SYNC_IBUF_TREE_NODE: |
939 | |
940 | ut_a(find(latches, SYNC_IBUF_INDEX_TREE) != 0 |
941 | || basic_check(latches, level, SYNC_IBUF_TREE_NODE - 1)); |
942 | break; |
943 | |
944 | case SYNC_IBUF_TREE_NODE_NEW: |
945 | |
946 | /* ibuf_add_free_page() allocates new pages for the change |
947 | buffer while only holding the tablespace x-latch. These |
948 | pre-allocated new pages may only be used while holding |
949 | ibuf_mutex, in btr_page_alloc_for_ibuf(). */ |
950 | |
951 | ut_a(find(latches, SYNC_IBUF_MUTEX) != 0 |
952 | || find(latches, SYNC_FSP) != 0); |
953 | break; |
954 | |
955 | case SYNC_IBUF_INDEX_TREE: |
956 | |
957 | if (find(latches, SYNC_FSP) != 0) { |
958 | basic_check(latches, level, level - 1); |
959 | } else { |
960 | basic_check(latches, level, SYNC_IBUF_TREE_NODE - 1); |
961 | } |
962 | break; |
963 | |
964 | case SYNC_IBUF_PESS_INSERT_MUTEX: |
965 | |
966 | basic_check(latches, level, SYNC_FSP - 1); |
967 | ut_a(find(latches, SYNC_IBUF_MUTEX) == 0); |
968 | break; |
969 | |
970 | case SYNC_IBUF_HEADER: |
971 | |
972 | basic_check(latches, level, SYNC_FSP - 1); |
973 | ut_a(find(latches, SYNC_IBUF_MUTEX) == NULL); |
974 | ut_a(find(latches, SYNC_IBUF_PESS_INSERT_MUTEX) == NULL); |
975 | break; |
976 | |
977 | case SYNC_DICT: |
978 | basic_check(latches, level, SYNC_DICT); |
979 | break; |
980 | |
981 | case SYNC_MUTEX: |
982 | case SYNC_UNKNOWN: |
983 | case SYNC_LEVEL_VARYING: |
984 | case RW_LOCK_X: |
985 | case RW_LOCK_X_WAIT: |
986 | case RW_LOCK_S: |
987 | case RW_LOCK_SX: |
988 | case RW_LOCK_NOT_LOCKED: |
989 | /* These levels should never be set for a latch. */ |
990 | ut_error; |
991 | break; |
992 | } |
993 | |
994 | return(latches); |
995 | } |
996 | |
997 | /** Removes a latch from the thread level array if it is found there. |
998 | @param[in] latch that was released/unlocked |
999 | @param[in] level level of the latch |
1000 | @return true if found in the array; it is not an error if the latch is |
1001 | not found, as we presently are not able to determine the level for |
1002 | every latch reservation the program does */ |
1003 | void |
1004 | LatchDebug::unlock(const latch_t* latch) |
1005 | UNIV_NOTHROW |
1006 | { |
1007 | if (latch->get_level() == SYNC_LEVEL_VARYING) { |
1008 | // We don't have varying level mutexes |
1009 | ut_ad(latch->m_rw_lock); |
1010 | } |
1011 | |
1012 | Latches* latches; |
1013 | |
1014 | if (*latch->get_name() == '.') { |
1015 | |
1016 | /* Ignore diagnostic latches, starting with '.' */ |
1017 | |
1018 | } else if ((latches = thread_latches()) != NULL) { |
1019 | |
1020 | Latches::reverse_iterator rend = latches->rend(); |
1021 | |
1022 | for (Latches::reverse_iterator it = latches->rbegin(); |
1023 | it != rend; |
1024 | ++it) { |
1025 | |
1026 | if (it->m_latch != latch) { |
1027 | |
1028 | continue; |
1029 | } |
1030 | |
1031 | Latches::iterator i = it.base(); |
1032 | |
1033 | latches->erase(--i); |
1034 | |
1035 | /* If this thread doesn't own any more |
1036 | latches remove from the map. |
1037 | |
1038 | FIXME: Perhaps use the master thread |
1039 | to do purge. Or, do it from close connection. |
1040 | This could be expensive. */ |
1041 | |
1042 | if (latches->empty()) { |
1043 | |
1044 | m_mutex.enter(); |
1045 | |
1046 | os_thread_id_t thread_id; |
1047 | |
1048 | thread_id = os_thread_get_curr_id(); |
1049 | |
1050 | m_threads.erase(thread_id); |
1051 | |
1052 | m_mutex.exit(); |
1053 | |
1054 | UT_DELETE(latches); |
1055 | } |
1056 | |
1057 | return; |
1058 | } |
1059 | |
1060 | if (latch->get_level() != SYNC_LEVEL_VARYING) { |
1061 | ib::error() |
1062 | << "Couldn't find latch " |
1063 | << sync_latch_get_name(latch->get_id()); |
1064 | |
1065 | print_latches(latches); |
1066 | |
1067 | /** Must find the latch. */ |
1068 | ut_error; |
1069 | } |
1070 | } |
1071 | } |
1072 | |
1073 | /** Get the latch id from a latch name. |
1074 | @param[in] name Latch name |
1075 | @return latch id if found else LATCH_ID_NONE. */ |
1076 | latch_id_t |
1077 | sync_latch_get_id(const char* name) |
1078 | { |
1079 | LatchMetaData::const_iterator end = latch_meta.end(); |
1080 | |
1081 | /* Linear scan should be OK, this should be extremely rare. */ |
1082 | |
1083 | for (LatchMetaData::const_iterator it = latch_meta.begin(); |
1084 | it != end; |
1085 | ++it) { |
1086 | |
1087 | if (*it == NULL || (*it)->get_id() == LATCH_ID_NONE) { |
1088 | |
1089 | continue; |
1090 | |
1091 | } else if (strcmp((*it)->get_name(), name) == 0) { |
1092 | |
1093 | return((*it)->get_id()); |
1094 | } |
1095 | } |
1096 | |
1097 | return(LATCH_ID_NONE); |
1098 | } |
1099 | |
1100 | /** Get the latch name from a sync level |
1101 | @param[in] level Latch level to lookup |
1102 | @return NULL if not found. */ |
1103 | const char* |
1104 | sync_latch_get_name(latch_level_t level) |
1105 | { |
1106 | LatchMetaData::const_iterator end = latch_meta.end(); |
1107 | |
1108 | /* Linear scan should be OK, this should be extremely rare. */ |
1109 | |
1110 | for (LatchMetaData::const_iterator it = latch_meta.begin(); |
1111 | it != end; |
1112 | ++it) { |
1113 | |
1114 | if (*it == NULL || (*it)->get_id() == LATCH_ID_NONE) { |
1115 | |
1116 | continue; |
1117 | |
1118 | } else if ((*it)->get_level() == level) { |
1119 | |
1120 | return((*it)->get_name()); |
1121 | } |
1122 | } |
1123 | |
1124 | return(0); |
1125 | } |
1126 | |
1127 | /** Check if it is OK to acquire the latch. |
1128 | @param[in] latch latch type */ |
1129 | void |
1130 | sync_check_lock_validate(const latch_t* latch) |
1131 | { |
1132 | if (LatchDebug::instance() != NULL) { |
1133 | LatchDebug::instance()->lock_validate( |
1134 | latch, latch->get_level()); |
1135 | } |
1136 | } |
1137 | |
1138 | /** Note that the lock has been granted |
1139 | @param[in] latch latch type */ |
1140 | void |
1141 | sync_check_lock_granted(const latch_t* latch) |
1142 | { |
1143 | if (LatchDebug::instance() != NULL) { |
1144 | LatchDebug::instance()->lock_granted(latch, latch->get_level()); |
1145 | } |
1146 | } |
1147 | |
1148 | /** Check if it is OK to acquire the latch. |
1149 | @param[in] latch latch type |
1150 | @param[in] level Latch level */ |
1151 | void |
1152 | sync_check_lock( |
1153 | const latch_t* latch, |
1154 | latch_level_t level) |
1155 | { |
1156 | if (LatchDebug::instance() != NULL) { |
1157 | |
1158 | ut_ad(latch->get_level() == SYNC_LEVEL_VARYING); |
1159 | ut_ad(latch->get_id() == LATCH_ID_BUF_BLOCK_LOCK); |
1160 | |
1161 | LatchDebug::instance()->lock_validate(latch, level); |
1162 | LatchDebug::instance()->lock_granted(latch, level); |
1163 | } |
1164 | } |
1165 | |
1166 | /** Check if it is OK to re-acquire the lock. |
1167 | @param[in] latch RW-LOCK to relock (recursive X locks) */ |
1168 | void |
1169 | sync_check_relock(const latch_t* latch) |
1170 | { |
1171 | if (LatchDebug::instance() != NULL) { |
1172 | LatchDebug::instance()->relock(latch); |
1173 | } |
1174 | } |
1175 | |
1176 | /** Removes a latch from the thread level array if it is found there. |
1177 | @param[in] latch The latch to unlock */ |
1178 | void |
1179 | sync_check_unlock(const latch_t* latch) |
1180 | { |
1181 | if (LatchDebug::instance() != NULL) { |
1182 | LatchDebug::instance()->unlock(latch); |
1183 | } |
1184 | } |
1185 | |
1186 | /** Checks if the level array for the current thread contains a |
1187 | mutex or rw-latch at the specified level. |
1188 | @param[in] level to find |
1189 | @return a matching latch, or NULL if not found */ |
1190 | const latch_t* |
1191 | sync_check_find(latch_level_t level) |
1192 | { |
1193 | if (LatchDebug::instance() != NULL) { |
1194 | return(LatchDebug::instance()->find(level)); |
1195 | } |
1196 | |
1197 | return(NULL); |
1198 | } |
1199 | |
1200 | /** Iterate over the thread's latches. |
1201 | @param[in,out] functor called for each element. |
1202 | @return true if the functor returns true for any element */ |
1203 | bool |
1204 | sync_check_iterate(const sync_check_functor_t& functor) |
1205 | { |
1206 | if (LatchDebug* debug = LatchDebug::instance()) { |
1207 | return(debug->for_each(functor)); |
1208 | } |
1209 | |
1210 | return(false); |
1211 | } |
1212 | |
1213 | /** Enable sync order checking. |
1214 | |
1215 | Note: We don't enforce any synchronisation checks. The caller must ensure |
1216 | that no races can occur */ |
1217 | void |
1218 | sync_check_enable() |
1219 | { |
1220 | if (!srv_sync_debug) { |
1221 | |
1222 | return; |
1223 | } |
1224 | |
1225 | /* We should always call this before we create threads. */ |
1226 | |
1227 | LatchDebug::create_instance(); |
1228 | } |
1229 | |
1230 | /** Initialise the debug data structures */ |
1231 | void |
1232 | LatchDebug::init() |
1233 | UNIV_NOTHROW |
1234 | { |
1235 | mutex_create(LATCH_ID_RW_LOCK_DEBUG, &rw_lock_debug_mutex); |
1236 | } |
1237 | |
1238 | /** Shutdown the latch debug checking |
1239 | |
1240 | Note: We don't enforce any synchronisation checks. The caller must ensure |
1241 | that no races can occur */ |
1242 | void |
1243 | LatchDebug::shutdown() |
1244 | UNIV_NOTHROW |
1245 | { |
1246 | mutex_free(&rw_lock_debug_mutex); |
1247 | |
1248 | ut_a(s_initialized); |
1249 | |
1250 | s_initialized = false; |
1251 | |
1252 | UT_DELETE(s_instance); |
1253 | |
1254 | LatchDebug::s_instance = NULL; |
1255 | } |
1256 | |
1257 | /** Acquires the debug mutex. We cannot use the mutex defined in sync0sync, |
1258 | because the debug mutex is also acquired in sync0arr while holding the OS |
1259 | mutex protecting the sync array, and the ordinary mutex_enter might |
1260 | recursively call routines in sync0arr, leading to a deadlock on the OS |
1261 | mutex. */ |
1262 | void |
1263 | rw_lock_debug_mutex_enter() |
1264 | { |
1265 | mutex_enter(&rw_lock_debug_mutex); |
1266 | } |
1267 | |
1268 | /** Releases the debug mutex. */ |
1269 | void |
1270 | rw_lock_debug_mutex_exit() |
1271 | { |
1272 | mutex_exit(&rw_lock_debug_mutex); |
1273 | } |
1274 | #endif /* UNIV_DEBUG */ |
1275 | |
1276 | /* Meta data for all the InnoDB latches. If the latch is not in recorded |
1277 | here then it will be be considered for deadlock checks. */ |
1278 | LatchMetaData latch_meta; |
1279 | |
1280 | /** Load the latch meta data. */ |
1281 | static |
1282 | void |
1283 | sync_latch_meta_init() |
1284 | UNIV_NOTHROW |
1285 | { |
1286 | latch_meta.resize(LATCH_ID_MAX); |
1287 | |
1288 | /* The latches should be ordered on latch_id_t. So that we can |
1289 | index directly into the vector to update and fetch meta-data. */ |
1290 | |
1291 | LATCH_ADD_MUTEX(AUTOINC, SYNC_DICT_AUTOINC_MUTEX, autoinc_mutex_key); |
1292 | |
1293 | #if defined PFS_SKIP_BUFFER_MUTEX_RWLOCK || defined PFS_GROUP_BUFFER_SYNC |
1294 | LATCH_ADD_MUTEX(BUF_BLOCK_MUTEX, SYNC_BUF_BLOCK, PFS_NOT_INSTRUMENTED); |
1295 | #else |
1296 | LATCH_ADD_MUTEX(BUF_BLOCK_MUTEX, SYNC_BUF_BLOCK, |
1297 | buffer_block_mutex_key); |
1298 | #endif /* PFS_SKIP_BUFFER_MUTEX_RWLOCK || PFS_GROUP_BUFFER_SYNC */ |
1299 | |
1300 | LATCH_ADD_MUTEX(BUF_POOL, SYNC_BUF_POOL, buf_pool_mutex_key); |
1301 | |
1302 | LATCH_ADD_MUTEX(BUF_POOL_ZIP, SYNC_BUF_BLOCK, buf_pool_zip_mutex_key); |
1303 | |
1304 | LATCH_ADD_MUTEX(CACHE_LAST_READ, SYNC_TRX_I_S_LAST_READ, |
1305 | cache_last_read_mutex_key); |
1306 | |
1307 | LATCH_ADD_MUTEX(DICT_FOREIGN_ERR, SYNC_NO_ORDER_CHECK, |
1308 | dict_foreign_err_mutex_key); |
1309 | |
1310 | LATCH_ADD_MUTEX(DICT_SYS, SYNC_DICT, dict_sys_mutex_key); |
1311 | |
1312 | LATCH_ADD_MUTEX(FIL_SYSTEM, SYNC_ANY_LATCH, fil_system_mutex_key); |
1313 | |
1314 | LATCH_ADD_MUTEX(FLUSH_LIST, SYNC_BUF_FLUSH_LIST, flush_list_mutex_key); |
1315 | |
1316 | LATCH_ADD_MUTEX(FTS_BG_THREADS, SYNC_FTS_BG_THREADS, |
1317 | fts_bg_threads_mutex_key); |
1318 | |
1319 | LATCH_ADD_MUTEX(FTS_DELETE, SYNC_FTS_OPTIMIZE, fts_delete_mutex_key); |
1320 | |
1321 | LATCH_ADD_MUTEX(FTS_OPTIMIZE, SYNC_FTS_OPTIMIZE, |
1322 | fts_optimize_mutex_key); |
1323 | |
1324 | LATCH_ADD_MUTEX(FTS_DOC_ID, SYNC_FTS_OPTIMIZE, fts_doc_id_mutex_key); |
1325 | |
1326 | LATCH_ADD_MUTEX(FTS_PLL_TOKENIZE, SYNC_FTS_TOKENIZE, |
1327 | fts_pll_tokenize_mutex_key); |
1328 | |
1329 | LATCH_ADD_MUTEX(HASH_TABLE_MUTEX, SYNC_BUF_PAGE_HASH, |
1330 | hash_table_mutex_key); |
1331 | |
1332 | LATCH_ADD_MUTEX(IBUF_BITMAP, SYNC_IBUF_BITMAP_MUTEX, |
1333 | ibuf_bitmap_mutex_key); |
1334 | |
1335 | LATCH_ADD_MUTEX(IBUF, SYNC_IBUF_MUTEX, ibuf_mutex_key); |
1336 | |
1337 | LATCH_ADD_MUTEX(IBUF_PESSIMISTIC_INSERT, SYNC_IBUF_PESS_INSERT_MUTEX, |
1338 | ibuf_pessimistic_insert_mutex_key); |
1339 | |
1340 | LATCH_ADD_MUTEX(LOG_SYS, SYNC_LOG, log_sys_mutex_key); |
1341 | |
1342 | LATCH_ADD_MUTEX(LOG_WRITE, SYNC_LOG_WRITE, log_sys_write_mutex_key); |
1343 | |
1344 | LATCH_ADD_MUTEX(LOG_FLUSH_ORDER, SYNC_LOG_FLUSH_ORDER, |
1345 | log_flush_order_mutex_key); |
1346 | |
1347 | LATCH_ADD_MUTEX(MUTEX_LIST, SYNC_NO_ORDER_CHECK, mutex_list_mutex_key); |
1348 | |
1349 | LATCH_ADD_MUTEX(PAGE_CLEANER, SYNC_PAGE_CLEANER, |
1350 | page_cleaner_mutex_key); |
1351 | |
1352 | LATCH_ADD_MUTEX(PURGE_SYS_PQ, SYNC_PURGE_QUEUE, |
1353 | purge_sys_pq_mutex_key); |
1354 | |
1355 | LATCH_ADD_MUTEX(RECALC_POOL, SYNC_STATS_AUTO_RECALC, |
1356 | recalc_pool_mutex_key); |
1357 | |
1358 | LATCH_ADD_MUTEX(RECV_SYS, SYNC_RECV, recv_sys_mutex_key); |
1359 | |
1360 | LATCH_ADD_MUTEX(RECV_WRITER, SYNC_RECV_WRITER, recv_writer_mutex_key); |
1361 | |
1362 | LATCH_ADD_MUTEX(REDO_RSEG, SYNC_REDO_RSEG, redo_rseg_mutex_key); |
1363 | |
1364 | LATCH_ADD_MUTEX(NOREDO_RSEG, SYNC_NOREDO_RSEG, noredo_rseg_mutex_key); |
1365 | |
1366 | #ifdef UNIV_DEBUG |
1367 | /* Mutex names starting with '.' are not tracked. They are assumed |
1368 | to be diagnostic mutexes used in debugging. */ |
1369 | latch_meta[LATCH_ID_RW_LOCK_DEBUG] = |
1370 | LATCH_ADD_MUTEX(RW_LOCK_DEBUG, |
1371 | SYNC_NO_ORDER_CHECK, |
1372 | rw_lock_debug_mutex_key); |
1373 | #endif /* UNIV_DEBUG */ |
1374 | |
1375 | LATCH_ADD_MUTEX(RTR_SSN_MUTEX, SYNC_ANY_LATCH, rtr_ssn_mutex_key); |
1376 | |
1377 | LATCH_ADD_MUTEX(RTR_ACTIVE_MUTEX, SYNC_ANY_LATCH, |
1378 | rtr_active_mutex_key); |
1379 | |
1380 | LATCH_ADD_MUTEX(RTR_MATCH_MUTEX, SYNC_ANY_LATCH, rtr_match_mutex_key); |
1381 | |
1382 | LATCH_ADD_MUTEX(RTR_PATH_MUTEX, SYNC_ANY_LATCH, rtr_path_mutex_key); |
1383 | |
1384 | LATCH_ADD_MUTEX(RW_LOCK_LIST, SYNC_NO_ORDER_CHECK, |
1385 | rw_lock_list_mutex_key); |
1386 | |
1387 | LATCH_ADD_MUTEX(RW_LOCK_MUTEX, SYNC_NO_ORDER_CHECK, rw_lock_mutex_key); |
1388 | |
1389 | LATCH_ADD_MUTEX(SRV_INNODB_MONITOR, SYNC_NO_ORDER_CHECK, |
1390 | srv_innodb_monitor_mutex_key); |
1391 | |
1392 | LATCH_ADD_MUTEX(SRV_MISC_TMPFILE, SYNC_ANY_LATCH, |
1393 | srv_misc_tmpfile_mutex_key); |
1394 | |
1395 | LATCH_ADD_MUTEX(SRV_MONITOR_FILE, SYNC_NO_ORDER_CHECK, |
1396 | srv_monitor_file_mutex_key); |
1397 | |
1398 | LATCH_ADD_MUTEX(BUF_DBLWR, SYNC_DOUBLEWRITE, buf_dblwr_mutex_key); |
1399 | |
1400 | LATCH_ADD_MUTEX(TRX_POOL, SYNC_POOL, trx_pool_mutex_key); |
1401 | |
1402 | LATCH_ADD_MUTEX(TRX_POOL_MANAGER, SYNC_POOL_MANAGER, |
1403 | trx_pool_manager_mutex_key); |
1404 | |
1405 | LATCH_ADD_MUTEX(TRX, SYNC_TRX, trx_mutex_key); |
1406 | |
1407 | LATCH_ADD_MUTEX(LOCK_SYS, SYNC_LOCK_SYS, lock_mutex_key); |
1408 | |
1409 | LATCH_ADD_MUTEX(LOCK_SYS_WAIT, SYNC_LOCK_WAIT_SYS, |
1410 | lock_wait_mutex_key); |
1411 | |
1412 | LATCH_ADD_MUTEX(TRX_SYS, SYNC_TRX_SYS, trx_sys_mutex_key); |
1413 | |
1414 | LATCH_ADD_MUTEX(SRV_SYS, SYNC_THREADS, srv_sys_mutex_key); |
1415 | |
1416 | LATCH_ADD_MUTEX(SRV_SYS_TASKS, SYNC_ANY_LATCH, srv_threads_mutex_key); |
1417 | |
1418 | LATCH_ADD_MUTEX(PAGE_ZIP_STAT_PER_INDEX, SYNC_ANY_LATCH, |
1419 | page_zip_stat_per_index_mutex_key); |
1420 | |
1421 | #ifndef PFS_SKIP_EVENT_MUTEX |
1422 | LATCH_ADD_MUTEX(EVENT_MANAGER, SYNC_NO_ORDER_CHECK, |
1423 | event_manager_mutex_key); |
1424 | #else |
1425 | LATCH_ADD_MUTEX(EVENT_MANAGER, SYNC_NO_ORDER_CHECK, |
1426 | PFS_NOT_INSTRUMENTED); |
1427 | #endif /* !PFS_SKIP_EVENT_MUTEX */ |
1428 | |
1429 | LATCH_ADD_MUTEX(EVENT_MUTEX, SYNC_NO_ORDER_CHECK, event_mutex_key); |
1430 | |
1431 | LATCH_ADD_MUTEX(SYNC_ARRAY_MUTEX, SYNC_NO_ORDER_CHECK, |
1432 | sync_array_mutex_key); |
1433 | |
1434 | LATCH_ADD_MUTEX(ZIP_PAD_MUTEX, SYNC_NO_ORDER_CHECK, zip_pad_mutex_key); |
1435 | |
1436 | LATCH_ADD_MUTEX(OS_AIO_READ_MUTEX, SYNC_NO_ORDER_CHECK, |
1437 | PFS_NOT_INSTRUMENTED); |
1438 | |
1439 | LATCH_ADD_MUTEX(OS_AIO_WRITE_MUTEX, SYNC_NO_ORDER_CHECK, |
1440 | PFS_NOT_INSTRUMENTED); |
1441 | |
1442 | LATCH_ADD_MUTEX(OS_AIO_LOG_MUTEX, SYNC_NO_ORDER_CHECK, |
1443 | PFS_NOT_INSTRUMENTED); |
1444 | |
1445 | LATCH_ADD_MUTEX(OS_AIO_IBUF_MUTEX, SYNC_NO_ORDER_CHECK, |
1446 | PFS_NOT_INSTRUMENTED); |
1447 | |
1448 | LATCH_ADD_MUTEX(OS_AIO_SYNC_MUTEX, SYNC_NO_ORDER_CHECK, |
1449 | PFS_NOT_INSTRUMENTED); |
1450 | |
1451 | LATCH_ADD_MUTEX(ROW_DROP_LIST, SYNC_NO_ORDER_CHECK, |
1452 | row_drop_list_mutex_key); |
1453 | |
1454 | LATCH_ADD_MUTEX(INDEX_ONLINE_LOG, SYNC_INDEX_ONLINE_LOG, |
1455 | index_online_log_key); |
1456 | |
1457 | LATCH_ADD_MUTEX(WORK_QUEUE, SYNC_WORK_QUEUE, PFS_NOT_INSTRUMENTED); |
1458 | |
1459 | // Add the RW locks |
1460 | LATCH_ADD_RWLOCK(BTR_SEARCH, SYNC_SEARCH_SYS, btr_search_latch_key); |
1461 | |
1462 | LATCH_ADD_RWLOCK(BUF_BLOCK_LOCK, SYNC_LEVEL_VARYING, |
1463 | buf_block_lock_key); |
1464 | |
1465 | #ifdef UNIV_DEBUG |
1466 | LATCH_ADD_RWLOCK(BUF_BLOCK_DEBUG, SYNC_LEVEL_VARYING, |
1467 | buf_block_debug_latch_key); |
1468 | #endif /* UNIV_DEBUG */ |
1469 | |
1470 | LATCH_ADD_RWLOCK(DICT_OPERATION, SYNC_DICT_OPERATION, |
1471 | dict_operation_lock_key); |
1472 | |
1473 | LATCH_ADD_RWLOCK(CHECKPOINT, SYNC_NO_ORDER_CHECK, checkpoint_lock_key); |
1474 | |
1475 | LATCH_ADD_RWLOCK(FIL_SPACE, SYNC_FSP, fil_space_latch_key); |
1476 | |
1477 | LATCH_ADD_RWLOCK(FTS_CACHE, SYNC_FTS_CACHE, fts_cache_rw_lock_key); |
1478 | |
1479 | LATCH_ADD_RWLOCK(FTS_CACHE_INIT, SYNC_FTS_CACHE_INIT, |
1480 | fts_cache_init_rw_lock_key); |
1481 | |
1482 | LATCH_ADD_RWLOCK(TRX_I_S_CACHE, SYNC_TRX_I_S_RWLOCK, |
1483 | trx_i_s_cache_lock_key); |
1484 | |
1485 | LATCH_ADD_RWLOCK(TRX_PURGE, SYNC_PURGE_LATCH, trx_purge_latch_key); |
1486 | |
1487 | LATCH_ADD_RWLOCK(IBUF_INDEX_TREE, SYNC_IBUF_INDEX_TREE, |
1488 | index_tree_rw_lock_key); |
1489 | |
1490 | LATCH_ADD_RWLOCK(INDEX_TREE, SYNC_INDEX_TREE, index_tree_rw_lock_key); |
1491 | |
1492 | LATCH_ADD_RWLOCK(DICT_TABLE_STATS, SYNC_INDEX_TREE, |
1493 | dict_table_stats_key); |
1494 | |
1495 | LATCH_ADD_RWLOCK(HASH_TABLE_RW_LOCK, SYNC_BUF_PAGE_HASH, |
1496 | hash_table_locks_key); |
1497 | |
1498 | LATCH_ADD_MUTEX(SYNC_DEBUG_MUTEX, SYNC_NO_ORDER_CHECK, |
1499 | PFS_NOT_INSTRUMENTED); |
1500 | |
1501 | /* JAN: TODO: Add PFS instrumentation */ |
1502 | LATCH_ADD_MUTEX(SCRUB_STAT_MUTEX, SYNC_NO_ORDER_CHECK, |
1503 | PFS_NOT_INSTRUMENTED); |
1504 | LATCH_ADD_MUTEX(DEFRAGMENT_MUTEX, SYNC_NO_ORDER_CHECK, |
1505 | PFS_NOT_INSTRUMENTED); |
1506 | LATCH_ADD_MUTEX(BTR_DEFRAGMENT_MUTEX, SYNC_NO_ORDER_CHECK, |
1507 | PFS_NOT_INSTRUMENTED); |
1508 | LATCH_ADD_MUTEX(FIL_CRYPT_MUTEX, SYNC_NO_ORDER_CHECK, |
1509 | PFS_NOT_INSTRUMENTED); |
1510 | LATCH_ADD_MUTEX(FIL_CRYPT_STAT_MUTEX, SYNC_NO_ORDER_CHECK, |
1511 | PFS_NOT_INSTRUMENTED); |
1512 | LATCH_ADD_MUTEX(FIL_CRYPT_DATA_MUTEX, SYNC_NO_ORDER_CHECK, |
1513 | PFS_NOT_INSTRUMENTED); |
1514 | LATCH_ADD_MUTEX(FIL_CRYPT_THREADS_MUTEX, SYNC_NO_ORDER_CHECK, |
1515 | PFS_NOT_INSTRUMENTED); |
1516 | LATCH_ADD_MUTEX(RW_TRX_HASH_ELEMENT, SYNC_RW_TRX_HASH_ELEMENT, |
1517 | rw_trx_hash_element_mutex_key); |
1518 | |
1519 | latch_id_t id = LATCH_ID_NONE; |
1520 | |
1521 | /* The array should be ordered on latch ID.We need to |
1522 | index directly into it from the mutex policy to update |
1523 | the counters and access the meta-data. */ |
1524 | |
1525 | for (LatchMetaData::iterator it = latch_meta.begin(); |
1526 | it != latch_meta.end(); |
1527 | ++it) { |
1528 | |
1529 | const latch_meta_t* meta = *it; |
1530 | |
1531 | |
1532 | /* Skip blank entries */ |
1533 | if (meta == NULL || meta->get_id() == LATCH_ID_NONE) { |
1534 | continue; |
1535 | } |
1536 | |
1537 | ut_a(id < meta->get_id()); |
1538 | |
1539 | id = meta->get_id(); |
1540 | } |
1541 | } |
1542 | |
1543 | /** Destroy the latch meta data */ |
1544 | static |
1545 | void |
1546 | sync_latch_meta_destroy() |
1547 | { |
1548 | for (LatchMetaData::iterator it = latch_meta.begin(); |
1549 | it != latch_meta.end(); |
1550 | ++it) { |
1551 | |
1552 | UT_DELETE(*it); |
1553 | } |
1554 | |
1555 | latch_meta.clear(); |
1556 | } |
1557 | |
1558 | /** Track mutex file creation name and line number. This is to avoid storing |
1559 | { const char* name; uint16_t line; } in every instance. This results in the |
1560 | sizeof(Mutex) > 64. We use a lookup table to store it separately. Fetching |
1561 | the values is very rare, only required for diagnostic purposes. And, we |
1562 | don't create/destroy mutexes that frequently. */ |
1563 | struct CreateTracker { |
1564 | |
1565 | /** Constructor */ |
1566 | CreateTracker() |
1567 | UNIV_NOTHROW |
1568 | { |
1569 | m_mutex.init(); |
1570 | } |
1571 | |
1572 | /** Destructor */ |
1573 | ~CreateTracker() |
1574 | UNIV_NOTHROW |
1575 | { |
1576 | ut_d(m_files.empty()); |
1577 | |
1578 | m_mutex.destroy(); |
1579 | } |
1580 | |
1581 | /** Register where the latch was created |
1582 | @param[in] ptr Latch instance |
1583 | @param[in] filename Where created |
1584 | @param[in] line Line number in filename */ |
1585 | void register_latch( |
1586 | const void* ptr, |
1587 | const char* filename, |
1588 | uint16_t line) |
1589 | UNIV_NOTHROW |
1590 | { |
1591 | m_mutex.enter(); |
1592 | |
1593 | Files::iterator lb = m_files.lower_bound(ptr); |
1594 | |
1595 | ut_ad(lb == m_files.end() |
1596 | || m_files.key_comp()(ptr, lb->first)); |
1597 | |
1598 | typedef Files::value_type value_type; |
1599 | |
1600 | m_files.insert(lb, value_type(ptr, File(filename, line))); |
1601 | |
1602 | m_mutex.exit(); |
1603 | } |
1604 | |
1605 | /** Deregister a latch - when it is destroyed |
1606 | @param[in] ptr Latch instance being destroyed */ |
1607 | void deregister_latch(const void* ptr) |
1608 | UNIV_NOTHROW |
1609 | { |
1610 | m_mutex.enter(); |
1611 | |
1612 | Files::iterator lb = m_files.lower_bound(ptr); |
1613 | |
1614 | ut_ad(lb != m_files.end() |
1615 | && !(m_files.key_comp()(ptr, lb->first))); |
1616 | |
1617 | m_files.erase(lb); |
1618 | |
1619 | m_mutex.exit(); |
1620 | } |
1621 | |
1622 | /** Get the create string, format is "name:line" |
1623 | @param[in] ptr Latch instance |
1624 | @return the create string or "" if not found */ |
1625 | std::string get(const void* ptr) |
1626 | UNIV_NOTHROW |
1627 | { |
1628 | m_mutex.enter(); |
1629 | |
1630 | std::string created; |
1631 | |
1632 | Files::iterator lb = m_files.lower_bound(ptr); |
1633 | |
1634 | if (lb != m_files.end() |
1635 | && !(m_files.key_comp()(ptr, lb->first))) { |
1636 | |
1637 | std::ostringstream msg; |
1638 | |
1639 | msg << lb->second.m_name << ":" << lb->second.m_line; |
1640 | |
1641 | created = msg.str(); |
1642 | } |
1643 | |
1644 | m_mutex.exit(); |
1645 | |
1646 | return(created); |
1647 | } |
1648 | |
1649 | private: |
1650 | /** For tracking the filename and line number */ |
1651 | struct File { |
1652 | |
1653 | /** Constructor */ |
1654 | File() UNIV_NOTHROW : m_name(), m_line() { } |
1655 | |
1656 | /** Constructor |
1657 | @param[in] name Filename where created |
1658 | @param[in] line Line number where created */ |
1659 | File(const char* name, uint16_t line) |
1660 | UNIV_NOTHROW |
1661 | : |
1662 | m_name(sync_basename(name)), |
1663 | m_line(line) |
1664 | { |
1665 | /* No op */ |
1666 | } |
1667 | |
1668 | /** Filename where created */ |
1669 | std::string m_name; |
1670 | |
1671 | /** Line number where created */ |
1672 | uint16_t m_line; |
1673 | }; |
1674 | |
1675 | /** Map the mutex instance to where it was created */ |
1676 | typedef std::map< |
1677 | const void*, |
1678 | File, |
1679 | std::less<const void*>, |
1680 | ut_allocator<std::pair<const void* const, File> > > |
1681 | Files; |
1682 | |
1683 | typedef OSMutex Mutex; |
1684 | |
1685 | /** Mutex protecting m_files */ |
1686 | Mutex m_mutex; |
1687 | |
1688 | /** Track the latch creation */ |
1689 | Files m_files; |
1690 | }; |
1691 | |
1692 | /** Track latch creation location. For reducing the size of the latches */ |
1693 | static CreateTracker create_tracker; |
1694 | |
1695 | /** Register a latch, called when it is created |
1696 | @param[in] ptr Latch instance that was created |
1697 | @param[in] filename Filename where it was created |
1698 | @param[in] line Line number in filename */ |
1699 | void |
1700 | sync_file_created_register( |
1701 | const void* ptr, |
1702 | const char* filename, |
1703 | uint16_t line) |
1704 | { |
1705 | create_tracker.register_latch(ptr, filename, line); |
1706 | } |
1707 | |
1708 | /** Deregister a latch, called when it is destroyed |
1709 | @param[in] ptr Latch to be destroyed */ |
1710 | void |
1711 | sync_file_created_deregister(const void* ptr) |
1712 | { |
1713 | create_tracker.deregister_latch(ptr); |
1714 | } |
1715 | |
1716 | /** Get the string where the file was created. Its format is "name:line" |
1717 | @param[in] ptr Latch instance |
1718 | @return created information or "" if can't be found */ |
1719 | std::string |
1720 | sync_file_created_get(const void* ptr) |
1721 | { |
1722 | return(create_tracker.get(ptr)); |
1723 | } |
1724 | |
1725 | /** Initializes the synchronization data structures. */ |
1726 | void |
1727 | sync_check_init() |
1728 | { |
1729 | ut_ad(!LatchDebug::s_initialized); |
1730 | ut_d(LatchDebug::s_initialized = true); |
1731 | |
1732 | sync_latch_meta_init(); |
1733 | |
1734 | /* Init the rw-lock & mutex list and create the mutex to protect it. */ |
1735 | |
1736 | UT_LIST_INIT(rw_lock_list, &rw_lock_t::list); |
1737 | |
1738 | mutex_create(LATCH_ID_RW_LOCK_LIST, &rw_lock_list_mutex); |
1739 | |
1740 | ut_d(LatchDebug::init()); |
1741 | |
1742 | sync_array_init(); |
1743 | } |
1744 | |
1745 | /** Free the InnoDB synchronization data structures. */ |
1746 | void |
1747 | sync_check_close() |
1748 | { |
1749 | ut_d(LatchDebug::shutdown()); |
1750 | |
1751 | mutex_free(&rw_lock_list_mutex); |
1752 | |
1753 | sync_array_close(); |
1754 | |
1755 | sync_latch_meta_destroy(); |
1756 | } |
1757 | |
1758 | |