File: ut0mutex.ic

package info (click to toggle)
mysql-8.0 8.0.44-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,272,892 kB
  • sloc: cpp: 4,685,345; ansic: 412,712; pascal: 108,395; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; python: 21,816; sh: 17,285; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,083; makefile: 1,793; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (227 lines) | stat: -rw-r--r-- 9,435 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*****************************************************************************

Copyright (c) 2013, 2025, Oracle and/or its affiliates.

Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
briefly in the InnoDB documentation. The contributions by Google are
incorporated with their permission, and subject to the conditions contained in
the file COPYING.Google.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.

This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation.  The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

*****************************************************************************/

/** @file include/ut0mutex.ic
 Mutex implementation include file

 Created 2012/08/21 Sunny Bains
 *******************************************************/

#include "sync0arr.h"
#include "sync0debug.h"

/**
Wait in the sync array.
@return true if the mutex acquisition was successful. */

template <template <typename> class Policy>
bool TTASEventMutex<Policy>::wait(const char *filename, uint32_t line,
                                  uint32_t spin) UNIV_NOTHROW {
  sync_cell_t *cell;
  sync_array_t *sync_arr;

  /* We shall prove that in C++14 as defined by the draft N4140 following holds:

  Theorem 1. If thread X decides to call os_event_wait(), and each thread which
  calls enter() eventually calls exit(), then X will eventually be woken up.

  Proof: By contradiction.
  We assume the contrary, that thread X got to endless sleep, so the following
  operations are in `sequenced-before` relation in X:

    # inside sync_array_get_and_reserve_cell(..) => sync_array_reserve_cell(..):
    X1 cell->signal_count = os_event_reset(..)
    # inside set_waiters():
    X2 m_waiters.store(true)
    # inside try_lock():
    X3 unsuccessful m_lock_word.compare_exchange_strong(false,true)
    # inside sync_array_wait_event(..):
    X4 os_event_wait_low(...,cell->signal_count)

  -----
  Lemma 1. All os_event_set()s `happen-before` X1.
  Proof of Lemma 1: By contradiction.
  The functions os_event_reset(), os_event_set() and os_event_wait_low() use
  internally the same mutex, so there must be a single objective order in which
  calls to them `synchronize-with` each other, and thus for each two such calls
  exactly one has to `happens-before` the other.

  If there is at least one call to os_event_set() which `happens-after` X1, then
  we can show that X will not sleep indefinitely, as either:
  a) os_event_set() `happens-before` X4, in which case it increments the event's
     signal_count, and os_event_wait_low() will notice that event's signal_count
     doesn't match the one from X1 and will not even start the sleep,
  b) X4 `happens-before` the os_event_set() in which case by the specification
     of os_event_set() it will wake up thread X.
  Contradiction with assumption that thread X goes to endless sleep ends the
  proof of Lemma 1.
  -----

  In what follows we will often refer to following rule from the standard:

  29.3.3. There shall be a single total order S on all memory_order_seq_cst
  operations, consistent with the `happens before` order and modification orders
  for all affected locations such that each memory_order_seq_cst operation B
  that loads a value from an atomic object M observes one of the following
  values:
  (3.1) the result of the last modification A of M that precedes B in S, if it
        exists, or
  (3.2) if A exists, the result of some modification of M that is not
        memory_order_seq_cst and that does not happen before A, or
  (3.3) if A does not exist, the result of some modification of M that is not
        memory_order_seq_cst.

  IMPORTANT: all modifications of `m_lock_word` and `m_waiters` in the current
  implementation use `memory_order_seq_cst`, so thankfully we can ignore cases
  (3.2) and (3.3) from considerations.
  IMPORTANT: In C++14 a failed compare_exchange_strong is considered a `load`,
  NOT a `read-modify-write`.

  X4 is only executed if X3 loaded m_lock_word==true, and according to 29.3.3.1
  this `true` must be a result of most recent modification of m_lock_word in S.

  We only write `true` to m_lock_word using compare_exchange_strong in enter(),
  so let Y be the last thread which performed this operation in try_lock():

    Y0  successful m_lock_word.compare_exchange_strong(false,true)

  before X3 in S. We've assumed Y must eventually call exit(), which involves:

    Y1  m_lock_word.store(false)
    Y2  if (m_waiters.load()) {
    Y3    m_waiters.store(false)
    Y4    os_event_set(..) }

  We will now show, that X2 is before Y2 in S.

  IMPORTANT: this is not a trivial thing: even though X3 apparently has not seen
  results of Y1 it does not follow immediately that X3 `happens-before` Y1. The
  C++14 standard deliberately doesn't make it easy to deduce `happens-before`
  from *absence* of evidence. We will only show that X3 is before Y1 in S, and
  leverage that to show that X2 is before Y2 in S, which among other things,
  requires X2, X3, Y1 and Y2 to use memory_order_seq_cst to be part of S at all.

  As Y0 is `sequenced-before` Y1, then Y0 also `happens-before` Y1 and thus
  Y1 must be after Y0 in S (29.3.3). As Y0 is the last modification of
  m_lock_word before X3 in S, it means that Y1 must be also after X3 in S.

  Y1 is `sequenced-before` Y2, thus Y1 `happens-before` Y2, thus Y1 is before
  Y2 in S.
  X2 is `sequenced-before` X3, thus X2 `happens-before` X3, thus X2 is before
  X3 in S.

  Combining all these, we get that X2 is before Y2 in S.

  Now, we will show that Y2 can not read `true` nor `false` as both lead to
  contradiction.

  If Y2 loads `true`, then Y will proceed to call os_event_set() during Y4.
  By Lemma 1 Y4 `happens-before` X1. But, Y2 is `sequenced-before` Y4,
  and X1 is `sequenced-before` X2, which together would give that Y2
  `happens-before` X2 which contradicts the rule that S is consistent with
  `happens-before`, and we've already established that X2 is before Y2 in S.

  So, it must be that Y2 loads `false`. There is only one place in code which
  writes `false` to this variable: during exit(). By 1.10.19 Y2 can not read
  value stored by itself in Y3 which `happens-after`, so this `false` must be
  stored by some other thread, Z, which was executing:

    Z3   m_waiters.store(false)
    Z4   os_event_set(..)

  By Lemma 1, Z4 `happens-before` X1, and since Z3 is `sequenced-before` Z4, and
  X1 is `sequenced-before` X2, we get Z3 `happens-before` X2.
  Thus, we get, that in S the order is: Z3, X2, Y2.

  We will now use 29.3.3.1 rule again for Y2=B, X2=A, M=m_waiters:
  Y2 must read he result of the last modification of m_waiters that
  precedes Y2 in S. This can not be Z3, because it is not the last modification
  before Y2, as we know X2 must appear somewhere between Z3 and Y2 in S.

  So, Y2 can not load `false` neither. Contradiction ends the proof.
  */

  sync_arr = sync_array_get_and_reserve_cell(
      this,
      (m_policy.get_id() == LATCH_ID_BUF_BLOCK_MUTEX ||
       m_policy.get_id() == LATCH_ID_BUF_POOL_ZIP)
          ? SYNC_BUF_BLOCK
          : SYNC_MUTEX,
      {filename, line}, &cell);

  /* The memory order of the array reservation and
  the change in the waiters field is important: when
  we suspend a thread, we first reserve the cell and
  then set waiters field to 1. When threads are released
  in mutex_exit, the waiters field is first set to zero
  and then the event is set to the signaled state. */

  set_waiters();

  /* Try to reserve still a few times. */

  for (uint32_t i = 0; i < spin; ++i) {
    if (try_lock()) {
      sync_array_free_cell(sync_arr, cell);

      /* Note that in this case we leave
      the waiters field set to 1. We cannot
      reset it to zero, as we do not know if
      there are other waiters. */

      return (true);
    }
  }

  /* Now we know that there has been some thread
  holding the mutex after the change in the wait
  array and the waiters field was made.  Now there
  is no risk of infinite wait on the event. */

  sync_array_wait_event(sync_arr, cell);

  return (false);
}

/** Wakeup any waiting thread(s). */

template <template <typename> class Policy>
void TTASEventMutex<Policy>::signal() UNIV_NOTHROW {
  clear_waiters();

  /* The memory order of resetting the waiters field and
  signaling the object is important. See Theorem 1 above. */
  os_event_set(m_event);

  sync_array_object_signalled();
}