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