1 /***************************************************************************** 2 3 Copyright (c) 2013, 2015, Oracle and/or its affiliates. All Rights Reserved. 4 Copyright (c) 2017, 2020, MariaDB Corporation. 5 6 This program is free software; you can redistribute it and/or modify it under 7 the terms of the GNU General Public License as published by the Free Software 8 Foundation; version 2 of the License. 9 10 This program is distributed in the hope that it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along with 15 this program; if not, write to the Free Software Foundation, Inc., 16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA 17 18 *****************************************************************************/ 19 20 /******************************************************************//** 21 @file include/ib0mutex.h 22 Policy based mutexes. 23 24 Created 2013-03-26 Sunny Bains. 25 ***********************************************************************/ 26 27 #ifndef UNIV_INNOCHECKSUM 28 29 #ifndef ib0mutex_h 30 #define ib0mutex_h 31 32 #include "my_cpu.h" 33 #include "os0event.h" 34 #include "sync0arr.h" 35 36 /** OS mutex for tracking lock/unlock for debugging */ 37 template <template <typename> class Policy> 38 struct OSTrackMutex { 39 40 typedef Policy<OSTrackMutex> MutexPolicy; 41 42 explicit OSTrackMutex(bool destroy_mutex_at_exit = true) 43 UNIV_NOTHROW 44 { 45 ut_d(m_freed = true); 46 ut_d(m_locked = false); 47 ut_d(m_destroy_at_exit = destroy_mutex_at_exit); 48 } 49 ~OSTrackMutexOSTrackMutex50 ~OSTrackMutex() UNIV_NOTHROW 51 { 52 ut_ad(!m_destroy_at_exit || !m_locked); 53 } 54 55 /** Initialise the mutex. */ initOSTrackMutex56 void init(latch_id_t, const char*, uint32_t) UNIV_NOTHROW 57 { 58 ut_ad(m_freed); 59 ut_ad(!m_locked); 60 61 m_mutex.init(); 62 63 ut_d(m_freed = false); 64 } 65 66 /** Destroy the mutex */ destroyOSTrackMutex67 void destroy() UNIV_NOTHROW 68 { 69 ut_ad(!m_locked); 70 ut_ad(!m_freed); 71 72 m_mutex.destroy(); 73 74 ut_d(m_freed = true); 75 } 76 77 /** Release the mutex. */ exitOSTrackMutex78 void exit() UNIV_NOTHROW 79 { 80 ut_ad(m_locked); 81 ut_d(m_locked = false); 82 ut_ad(!m_freed); 83 84 m_mutex.exit(); 85 } 86 87 /** Acquire the mutex. */ enterOSTrackMutex88 void enter(uint32_t, uint32_t, const char*, uint32_t) 89 UNIV_NOTHROW 90 { 91 ut_ad(!m_freed); 92 93 m_mutex.enter(); 94 95 ut_ad(!m_locked); 96 ut_d(m_locked = true); 97 } 98 99 /** @return true if locking succeeded */ try_lockOSTrackMutex100 bool try_lock() UNIV_NOTHROW 101 { 102 ut_ad(!m_freed); 103 104 bool locked = m_mutex.try_lock(); 105 106 if (locked) { 107 ut_ad(!m_locked); 108 ut_d(m_locked = locked); 109 } 110 111 return(locked); 112 } 113 114 /** @return non-const version of the policy */ policyOSTrackMutex115 MutexPolicy& policy() 116 UNIV_NOTHROW 117 { 118 return(m_policy); 119 } 120 121 /** @return the const version of the policy */ policyOSTrackMutex122 const MutexPolicy& policy() const 123 UNIV_NOTHROW 124 { 125 return(m_policy); 126 } 127 128 private: 129 #ifdef UNIV_DEBUG 130 /** true if the mutex has not be initialized */ 131 bool m_freed; 132 133 /** true if the mutex has been locked. */ 134 bool m_locked; 135 136 /** Do/Dont destroy mutex at exit */ 137 bool m_destroy_at_exit; 138 #endif /* UNIV_DEBUG */ 139 140 /** OS Mutex instance */ 141 OSMutex m_mutex; 142 143 /** Policy data */ 144 MutexPolicy m_policy; 145 }; 146 147 148 #ifdef HAVE_IB_LINUX_FUTEX 149 150 #include <linux/futex.h> 151 #include <sys/syscall.h> 152 153 /** Mutex implementation that used the Linux futex. */ 154 template <template <typename> class Policy> 155 struct TTASFutexMutex { 156 157 typedef Policy<TTASFutexMutex> MutexPolicy; 158 TTASFutexMutexTTASFutexMutex159 TTASFutexMutex() UNIV_NOTHROW 160 : 161 m_lock_word(MUTEX_STATE_UNLOCKED) 162 { 163 /* Check that lock_word is aligned. */ 164 ut_ad(!((ulint) &m_lock_word % sizeof(ulint))); 165 } 166 ~TTASFutexMutexTTASFutexMutex167 ~TTASFutexMutex() 168 { 169 ut_ad(m_lock_word.load(std::memory_order_relaxed) 170 == MUTEX_STATE_UNLOCKED); 171 } 172 173 /** Called when the mutex is "created". Note: Not from the constructor 174 but when the mutex is initialised. */ initTTASFutexMutex175 void init(latch_id_t, const char*, uint32_t) UNIV_NOTHROW 176 { 177 ut_ad(m_lock_word.load(std::memory_order_relaxed) 178 == MUTEX_STATE_UNLOCKED); 179 } 180 181 /** Destroy the mutex. */ destroyTTASFutexMutex182 void destroy() UNIV_NOTHROW 183 { 184 /* The destructor can be called at shutdown. */ 185 ut_ad(m_lock_word.load(std::memory_order_relaxed) 186 == MUTEX_STATE_UNLOCKED); 187 } 188 189 /** Acquire the mutex. 190 @param[in] max_spins max number of spins 191 @param[in] max_delay max delay per spin */ enterTTASFutexMutex192 void enter(uint32_t max_spins, uint32_t max_delay, 193 const char*, uint32_t) UNIV_NOTHROW 194 { 195 uint32_t n_spins, n_waits; 196 197 for (n_spins= 0; n_spins < max_spins; n_spins++) { 198 if (try_lock()) { 199 m_policy.add(n_spins, 0); 200 return; 201 } 202 203 ut_delay(max_delay); 204 } 205 206 for (n_waits= 0;; n_waits++) { 207 if (m_lock_word.exchange(MUTEX_STATE_WAITERS, 208 std::memory_order_acquire) 209 == MUTEX_STATE_UNLOCKED) { 210 break; 211 } 212 213 syscall(SYS_futex, &m_lock_word, 214 FUTEX_WAIT_PRIVATE, MUTEX_STATE_WAITERS, 215 0, 0, 0); 216 } 217 218 m_policy.add(n_spins, n_waits); 219 } 220 221 /** Release the mutex. */ exitTTASFutexMutex222 void exit() UNIV_NOTHROW 223 { 224 if (m_lock_word.exchange(MUTEX_STATE_UNLOCKED, 225 std::memory_order_release) 226 == MUTEX_STATE_WAITERS) { 227 syscall(SYS_futex, &m_lock_word, FUTEX_WAKE_PRIVATE, 228 1, 0, 0, 0); 229 } 230 } 231 232 /** Try and lock the mutex. 233 @return true if successful */ try_lockTTASFutexMutex234 bool try_lock() UNIV_NOTHROW 235 { 236 int32 oldval = MUTEX_STATE_UNLOCKED; 237 return m_lock_word.compare_exchange_strong( 238 oldval, 239 MUTEX_STATE_LOCKED, 240 std::memory_order_acquire, 241 std::memory_order_relaxed); 242 } 243 244 /** @return non-const version of the policy */ policyTTASFutexMutex245 MutexPolicy& policy() UNIV_NOTHROW 246 { 247 return(m_policy); 248 } 249 250 /** @return const version of the policy */ policyTTASFutexMutex251 const MutexPolicy& policy() const UNIV_NOTHROW 252 { 253 return(m_policy); 254 } 255 private: 256 /** Policy data */ 257 MutexPolicy m_policy; 258 259 /** lock_word is the target of the atomic test-and-set instruction 260 when atomic operations are enabled. */ 261 std::atomic<int32> m_lock_word; 262 }; 263 264 #endif /* HAVE_IB_LINUX_FUTEX */ 265 266 template <template <typename> class Policy> 267 struct TTASMutex { 268 269 typedef Policy<TTASMutex> MutexPolicy; 270 TTASMutexTTASMutex271 TTASMutex() UNIV_NOTHROW 272 : 273 m_lock_word(MUTEX_STATE_UNLOCKED) 274 { 275 /* Check that lock_word is aligned. */ 276 ut_ad(!((ulint) &m_lock_word % sizeof(ulint))); 277 } 278 ~TTASMutexTTASMutex279 ~TTASMutex() 280 { 281 ut_ad(m_lock_word.load(std::memory_order_relaxed) 282 == MUTEX_STATE_UNLOCKED); 283 } 284 285 /** Called when the mutex is "created". Note: Not from the constructor 286 but when the mutex is initialised. */ initTTASMutex287 void init(latch_id_t) UNIV_NOTHROW 288 { 289 ut_ad(m_lock_word.load(std::memory_order_relaxed) 290 == MUTEX_STATE_UNLOCKED); 291 } 292 293 /** Destroy the mutex. */ destroyTTASMutex294 void destroy() UNIV_NOTHROW 295 { 296 /* The destructor can be called at shutdown. */ 297 ut_ad(m_lock_word.load(std::memory_order_relaxed) 298 == MUTEX_STATE_UNLOCKED); 299 } 300 301 /** Try and lock the mutex. 302 @return true on success */ try_lockTTASMutex303 bool try_lock() UNIV_NOTHROW 304 { 305 uint32_t oldval = MUTEX_STATE_UNLOCKED; 306 return m_lock_word.compare_exchange_strong( 307 oldval, 308 MUTEX_STATE_LOCKED, 309 std::memory_order_acquire, 310 std::memory_order_relaxed); 311 } 312 313 /** Release the mutex. */ exitTTASMutex314 void exit() UNIV_NOTHROW 315 { 316 ut_ad(m_lock_word.load(std::memory_order_relaxed) 317 == MUTEX_STATE_LOCKED); 318 m_lock_word.store(MUTEX_STATE_UNLOCKED, 319 std::memory_order_release); 320 } 321 322 /** Acquire the mutex. 323 @param max_spins max number of spins 324 @param max_delay max delay per spin */ enterTTASMutex325 void enter(uint32_t max_spins, uint32_t max_delay, 326 const char*, uint32_t) UNIV_NOTHROW 327 { 328 const uint32_t step = max_spins; 329 uint32_t n_spins = 0; 330 331 while (!try_lock()) { 332 ut_delay(max_delay); 333 if (++n_spins == max_spins) { 334 os_thread_yield(); 335 max_spins+= step; 336 } 337 } 338 339 m_policy.add(n_spins, 0); 340 } 341 342 /** @return non-const version of the policy */ policyTTASMutex343 MutexPolicy& policy() UNIV_NOTHROW 344 { 345 return(m_policy); 346 } 347 348 /** @return const version of the policy */ policyTTASMutex349 const MutexPolicy& policy() const UNIV_NOTHROW 350 { 351 return(m_policy); 352 } 353 354 private: 355 // Disable copying 356 TTASMutex(const TTASMutex&); 357 TTASMutex& operator=(const TTASMutex&); 358 359 /** Policy data */ 360 MutexPolicy m_policy; 361 362 /** mutex state */ 363 std::atomic<uint32_t> m_lock_word; 364 }; 365 366 template <template <typename> class Policy> 367 struct TTASEventMutex { 368 369 typedef Policy<TTASEventMutex> MutexPolicy; 370 TTASEventMutexTTASEventMutex371 TTASEventMutex() 372 UNIV_NOTHROW 373 : 374 m_lock_word(MUTEX_STATE_UNLOCKED), 375 m_event() 376 { 377 /* Check that lock_word is aligned. */ 378 ut_ad(!((ulint) &m_lock_word % sizeof(ulint))); 379 } 380 ~TTASEventMutexTTASEventMutex381 ~TTASEventMutex() 382 UNIV_NOTHROW 383 { 384 ut_ad(state() == MUTEX_STATE_UNLOCKED); 385 } 386 387 /** Called when the mutex is "created". Note: Not from the constructor 388 but when the mutex is initialised. 389 @param[in] id Mutex ID */ initTTASEventMutex390 void init(latch_id_t id, const char*, uint32_t) UNIV_NOTHROW 391 { 392 ut_a(m_event == 0); 393 ut_ad(state() == MUTEX_STATE_UNLOCKED); 394 395 m_event = os_event_create(sync_latch_get_name(id)); 396 } 397 398 /** This is the real desctructor. This mutex can be created in BSS and 399 its desctructor will be called on exit(). We can't call 400 os_event_destroy() at that stage. */ destroyTTASEventMutex401 void destroy() 402 UNIV_NOTHROW 403 { 404 ut_ad(state() == MUTEX_STATE_UNLOCKED); 405 406 /* We have to free the event before InnoDB shuts down. */ 407 os_event_destroy(m_event); 408 m_event = 0; 409 } 410 411 /** Try and lock the mutex. Note: POSIX returns 0 on success. 412 @return true on success */ try_lockTTASEventMutex413 bool try_lock() 414 UNIV_NOTHROW 415 { 416 uint32_t oldval = MUTEX_STATE_UNLOCKED; 417 return m_lock_word.compare_exchange_strong( 418 oldval, 419 MUTEX_STATE_LOCKED, 420 std::memory_order_acquire, 421 std::memory_order_relaxed); 422 } 423 424 /** Release the mutex. */ exitTTASEventMutex425 void exit() 426 UNIV_NOTHROW 427 { 428 if (m_lock_word.exchange(MUTEX_STATE_UNLOCKED, 429 std::memory_order_release) 430 == MUTEX_STATE_WAITERS) { 431 os_event_set(m_event); 432 sync_array_object_signalled(); 433 } 434 } 435 436 /** Acquire the mutex. 437 @param[in] max_spins max number of spins 438 @param[in] max_delay max delay per spin 439 @param[in] filename from where called 440 @param[in] line within filename */ enterTTASEventMutex441 void enter( 442 uint32_t max_spins, 443 uint32_t max_delay, 444 const char* filename, 445 uint32_t line) 446 UNIV_NOTHROW 447 { 448 uint32_t n_spins = 0; 449 uint32_t n_waits = 0; 450 const uint32_t step = max_spins; 451 452 while (!try_lock()) { 453 if (n_spins++ == max_spins) { 454 max_spins += step; 455 n_waits++; 456 os_thread_yield(); 457 458 sync_cell_t* cell; 459 sync_array_t *sync_arr = sync_array_get_and_reserve_cell( 460 this, 461 (m_policy.get_id() == LATCH_ID_BUF_BLOCK_MUTEX 462 || m_policy.get_id() == LATCH_ID_BUF_POOL_ZIP) 463 ? SYNC_BUF_BLOCK 464 : SYNC_MUTEX, 465 filename, line, &cell); 466 467 uint32_t oldval = MUTEX_STATE_LOCKED; 468 m_lock_word.compare_exchange_strong( 469 oldval, 470 MUTEX_STATE_WAITERS, 471 std::memory_order_relaxed, 472 std::memory_order_relaxed); 473 474 if (oldval == MUTEX_STATE_UNLOCKED) { 475 sync_array_free_cell(sync_arr, cell); 476 } else { 477 sync_array_wait_event(sync_arr, cell); 478 } 479 } else { 480 ut_delay(max_delay); 481 } 482 } 483 484 m_policy.add(n_spins, n_waits); 485 } 486 487 /** @return the lock state. */ stateTTASEventMutex488 int32 state() const 489 UNIV_NOTHROW 490 { 491 return m_lock_word.load(std::memory_order_relaxed); 492 } 493 494 /** The event that the mutex will wait in sync0arr.cc 495 @return even instance */ eventTTASEventMutex496 os_event_t event() 497 UNIV_NOTHROW 498 { 499 return(m_event); 500 } 501 502 /** @return non-const version of the policy */ policyTTASEventMutex503 MutexPolicy& policy() 504 UNIV_NOTHROW 505 { 506 return(m_policy); 507 } 508 509 /** @return const version of the policy */ policyTTASEventMutex510 const MutexPolicy& policy() const 511 UNIV_NOTHROW 512 { 513 return(m_policy); 514 } 515 516 private: 517 /** Disable copying */ 518 TTASEventMutex(const TTASEventMutex&); 519 TTASEventMutex& operator=(const TTASEventMutex&); 520 521 /** mutex state */ 522 std::atomic<uint32_t> m_lock_word; 523 524 /** Used by sync0arr.cc for the wait queue */ 525 os_event_t m_event; 526 527 /** Policy data */ 528 MutexPolicy m_policy; 529 }; 530 531 /** Mutex interface for all policy mutexes. This class handles the interfacing 532 with the Performance Schema instrumentation. */ 533 template <typename MutexImpl> 534 struct PolicyMutex 535 { 536 typedef typename MutexImpl::MutexPolicy Policy; 537 PolicyMutexPolicyMutex538 PolicyMutex() UNIV_NOTHROW : m_impl() 539 { 540 #ifdef UNIV_PFS_MUTEX 541 m_ptr = 0; 542 #endif /* UNIV_PFS_MUTEX */ 543 } 544 ~PolicyMutexPolicyMutex545 ~PolicyMutex() { } 546 547 /** @return non-const version of the policy */ policyPolicyMutex548 Policy& policy() UNIV_NOTHROW 549 { 550 return(m_impl.policy()); 551 } 552 553 /** @return const version of the policy */ policyPolicyMutex554 const Policy& policy() const UNIV_NOTHROW 555 { 556 return(m_impl.policy()); 557 } 558 559 /** Release the mutex. */ exitPolicyMutex560 void exit() UNIV_NOTHROW 561 { 562 #ifdef UNIV_PFS_MUTEX 563 pfs_exit(); 564 #endif /* UNIV_PFS_MUTEX */ 565 566 ut_d(policy().context.release(m_impl)); 567 568 m_impl.exit(); 569 } 570 571 /** Acquire the mutex. 572 @param n_spins max number of spins 573 @param n_delay max delay per spin 574 @param name filename where locked 575 @param line line number where locked */ enterPolicyMutex576 void enter( 577 uint32_t n_spins, 578 uint32_t n_delay, 579 const char* name, 580 uint32_t line) UNIV_NOTHROW 581 { 582 #ifdef UNIV_PFS_MUTEX 583 /* Note: locker is really an alias for state. That's why 584 it has to be in the same scope during pfs_end(). */ 585 586 PSI_mutex_locker_state state; 587 PSI_mutex_locker* locker; 588 589 locker = pfs_begin_lock(&state, name, line); 590 #endif /* UNIV_PFS_MUTEX */ 591 592 ut_d(policy().context.enter(m_impl, name, line)); 593 594 m_impl.enter(n_spins, n_delay, name, line); 595 596 ut_d(policy().context.locked(m_impl, name, line)); 597 #ifdef UNIV_PFS_MUTEX 598 pfs_end(locker, 0); 599 #endif /* UNIV_PFS_MUTEX */ 600 } 601 602 /** Try and lock the mutex, return 0 on SUCCESS and 1 otherwise. 603 @param name filename where locked 604 @param line line number where locked */ trylockPolicyMutex605 int trylock(const char* name, uint32_t line) UNIV_NOTHROW 606 { 607 #ifdef UNIV_PFS_MUTEX 608 /* Note: locker is really an alias for state. That's why 609 it has to be in the same scope during pfs_end(). */ 610 611 PSI_mutex_locker_state state; 612 PSI_mutex_locker* locker; 613 614 locker = pfs_begin_trylock(&state, name, line); 615 #endif /* UNIV_PFS_MUTEX */ 616 617 /* There is a subtlety here, we check the mutex ordering 618 after locking here. This is only done to avoid add and 619 then remove if the trylock was unsuccesful. */ 620 621 int ret = m_impl.try_lock() ? 0 : 1; 622 623 if (ret == 0) { 624 625 ut_d(policy().context.enter(m_impl, name, line)); 626 627 ut_d(policy().context.locked(m_impl, name, line)); 628 } 629 630 #ifdef UNIV_PFS_MUTEX 631 pfs_end(locker, 0); 632 #endif /* UNIV_PFS_MUTEX */ 633 634 return(ret); 635 } 636 637 #ifdef UNIV_DEBUG 638 /** @return true if the thread owns the mutex. */ is_ownedPolicyMutex639 bool is_owned() const UNIV_NOTHROW 640 { 641 return(policy().context.is_owned()); 642 } 643 #endif /* UNIV_DEBUG */ 644 645 /** 646 Initialise the mutex. 647 648 @param[in] id Mutex ID 649 @param[in] filename file where created 650 @param[in] line line number in file where created */ initPolicyMutex651 void init( 652 latch_id_t id, 653 const char* filename, 654 uint32_t line) 655 UNIV_NOTHROW 656 { 657 #ifdef UNIV_PFS_MUTEX 658 pfs_add(sync_latch_get_pfs_key(id)); 659 #endif /* UNIV_PFS_MUTEX */ 660 661 m_impl.init(id, filename, line); 662 policy().init(m_impl, id, filename, line); 663 ut_d(policy().context.init(id)); 664 } 665 666 /** Free resources (if any) */ destroyPolicyMutex667 void destroy() UNIV_NOTHROW 668 { 669 #ifdef UNIV_PFS_MUTEX 670 pfs_del(); 671 #endif /* UNIV_PFS_MUTEX */ 672 m_impl.destroy(); 673 policy().destroy(); 674 ut_d(policy().context.destroy()); 675 } 676 677 /** Required for os_event_t */ 678 operator sys_mutex_t*() UNIV_NOTHROW 679 { 680 return(m_impl.operator sys_mutex_t*()); 681 } 682 683 #ifdef UNIV_PFS_MUTEX 684 /** Performance schema monitoring - register mutex with PFS. 685 686 Note: This is public only because we want to get around an issue 687 with registering a subset of buffer pool pages with PFS when 688 PFS_GROUP_BUFFER_SYNC is defined. Therefore this has to then 689 be called by external code (see buf0buf.cc). 690 691 @param key - Performance Schema key. */ pfs_addPolicyMutex692 void pfs_add(mysql_pfs_key_t key) UNIV_NOTHROW 693 { 694 ut_ad(m_ptr == 0); 695 m_ptr = PSI_MUTEX_CALL(init_mutex)(key, this); 696 } 697 698 private: 699 700 /** Performance schema monitoring. 701 @param state - PFS locker state 702 @param name - file name where locked 703 @param line - line number in file where locked */ pfs_begin_lockPolicyMutex704 PSI_mutex_locker* pfs_begin_lock( 705 PSI_mutex_locker_state* state, 706 const char* name, 707 uint32_t line) UNIV_NOTHROW 708 { 709 if (m_ptr != 0) { 710 return(PSI_MUTEX_CALL(start_mutex_wait)( 711 state, m_ptr, 712 PSI_MUTEX_LOCK, name, (uint) line)); 713 } 714 715 return(0); 716 } 717 718 /** Performance schema monitoring. 719 @param state - PFS locker state 720 @param name - file name where locked 721 @param line - line number in file where locked */ pfs_begin_trylockPolicyMutex722 PSI_mutex_locker* pfs_begin_trylock( 723 PSI_mutex_locker_state* state, 724 const char* name, 725 uint32_t line) UNIV_NOTHROW 726 { 727 if (m_ptr != 0) { 728 return(PSI_MUTEX_CALL(start_mutex_wait)( 729 state, m_ptr, 730 PSI_MUTEX_TRYLOCK, name, (uint) line)); 731 } 732 733 return(0); 734 } 735 736 /** Performance schema monitoring 737 @param locker - PFS identifier 738 @param ret - 0 for success and 1 for failure */ pfs_endPolicyMutex739 void pfs_end(PSI_mutex_locker* locker, int ret) UNIV_NOTHROW 740 { 741 if (locker != 0) { 742 PSI_MUTEX_CALL(end_mutex_wait)(locker, ret); 743 } 744 } 745 746 /** Performance schema monitoring - register mutex release */ pfs_exitPolicyMutex747 void pfs_exit() 748 { 749 if (m_ptr != 0) { 750 PSI_MUTEX_CALL(unlock_mutex)(m_ptr); 751 } 752 } 753 754 /** Performance schema monitoring - deregister */ pfs_delPolicyMutex755 void pfs_del() 756 { 757 if (m_ptr != 0) { 758 PSI_MUTEX_CALL(destroy_mutex)(m_ptr); 759 m_ptr = 0; 760 } 761 } 762 #endif /* UNIV_PFS_MUTEX */ 763 764 private: 765 /** The mutex implementation */ 766 MutexImpl m_impl; 767 768 #ifdef UNIV_PFS_MUTEX 769 /** The performance schema instrumentation hook. */ 770 PSI_mutex* m_ptr; 771 #endif /* UNIV_PFS_MUTEX */ 772 773 }; 774 775 #endif /* ib0mutex_h */ 776 777 #endif /* !UNIV_INNOCHECKSUM */ 778