File: shared_spin_lock.h

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (283 lines) | stat: -rw-r--r-- 9,994 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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/* Copyright (c) 2020, 2025, Oracle and/or its affiliates.

   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 */

#ifndef LOCK_SHARED_SPIN_LOCK_INCLUDED
#define LOCK_SHARED_SPIN_LOCK_INCLUDED

#include <atomic>
#include <map>
#include <memory>
#include <type_traits>

#include "sql/memory/aligned_atomic.h"

/**
  Provides atomic access in shared-exclusive modes. Shared mode allows for
  several threads to share lock acquisition. Exclusive mode will allow for
  a single thread to acquire the lock.

  The implementation also provides re-entrance, meaning that a thread is
  allowed to acquire the lock in the same mode several times without
  blocking. Re-entrance is symmetric, meaning, in the case the lock is
  acquired several times by the same thread, it should be released the same
  amount of times.

  Acquisition request priority management is implemented to avoid
  starvation, meaning:

  1) When no thread is holding the lock, acquisition is granted to the
     first thread to request it.

  2) If the lock is being held in shared mode and an exclusive acquisition
     request is made, no more shared or exclusive acquisition requests are
     granted until the exclusivity request is granted and released.

  The acquisition relation given to concurrent requests is as follows:

                   -------------------------------------------------------------
                   |              S2             |              E2             |
                   +-----------------------------+-----------------------------+
                   |   REQUEST    |   ACQUIRED   |   REQUEST    |   ACQUIRED   |
  -----------------+--------------+--------------------------------------------+
  |      | REQUEST |   S1 & S2    |   S1 & S2    |   S1 | E2    |      E2      |
  |  S1  |---------+--------------+--------------+--------------+--------------+
  |      | ACQUIRED|   S1 & S2    |   S1 & S2    |      S1      |      -       |
  -------+---------+--------------+--------------+--------------+--------------+
  |      | REQUEST |      E1      |      S2      |   E1 | E2    |      E2      |
  |  E1  |---------+--------------+--------------+--------------+--------------+
  |      | ACQUIRED|      E1      |      -       |      E1      |      -       |
  ------------------------------------------------------------------------------

  Legend:
  - S1: Thread that is requesting or has acquired in shared mode
  - S2: Thread that is requesting or has acquired in shared mode
  - E1: Thread that is requesting or has acquired in exclusive mode
  - E2: Thread that is requesting or has acquired in exclusive mode


 */
namespace lock {
class Shared_spin_lock {
 public:
  enum class enum_lock_acquisition {
    SL_EXCLUSIVE = 0,
    SL_SHARED = 1,
    SL_NO_ACQUISITION = 2
  };

  /**
    Sentry class for `Shared_spin_lock` to deliver RAII pattern usability.
   */
  class Guard {
   public:
    friend class Shared_spin_lock;

    /**
      Class constructor that receives the target spin-lock, whether or not
      it can be a shared acquisition and whether or not it should be a
      try-and-fail lock attempt, instead of a blocking attempt.

      @param target The target spin-lock.
      @param acquisition the acquisition type, SHARED, EXCLUSIVE or
             NO_ACQUISITION
      @param try_and_fail whether or not the lock attempt should be
                          blocking (only used if acquisition type is SHARED
                          or EXCLUSIVE).
     */
    Guard(Shared_spin_lock &target,
          enum_lock_acquisition acquisition = enum_lock_acquisition::SL_SHARED,
          bool try_and_fail = false);
    // Delete copy and move constructors
    Guard(Shared_spin_lock::Guard const &) = delete;
    Guard(Shared_spin_lock::Guard &&) = delete;
    //
    /**
      Destructor for the sentry. It will release any acquisition, shared or
      exclusive.
     */
    virtual ~Guard();

    // Delete copy and move operators
    Shared_spin_lock::Guard &operator=(Shared_spin_lock::Guard const &) =
        delete;
    Shared_spin_lock::Guard &operator=(Shared_spin_lock::Guard &&) = delete;
    //

    /**
      Arrow operator to access the underlying lock.

      @return A pointer to the underlying lock.
     */
    Shared_spin_lock *operator->();
    /**
      Star operator to access the underlying lock.

      @return A reference to the underlying lock.
     */
    Shared_spin_lock &operator*();
    /**
      If this instance was initialized without acquiring the lock
      (`NO_ACQUISITION ` passed to constructor) or the acquisition request
      wasn't granted (passing `try_and_fail = true` to the constructor),
      invoking this method will try to acquire the lock in the provided
      mode.

      @param acquisition the acquisition type, SHARED or EXCLUSIVE
      @param try_and_fail whether or not the lock attempt should be
                          blocking

      @return A reference to `this` object, for chaining purposes.
     */
    Shared_spin_lock::Guard &acquire(enum_lock_acquisition acquisition,
                                     bool try_and_fail = false);
    /**
      Releases the underlying lock acquisition, if any.

      @return A reference to `this` object, for chaining purposes.
     */
    Shared_spin_lock::Guard &release();

   private:
    /** The underlying lock */
    Shared_spin_lock &m_target;
    /** The type of lock acquisition to be requested */
    enum_lock_acquisition m_acquisition{enum_lock_acquisition::SL_SHARED};
  };
  friend class Shared_spin_lock::Guard;

  /**
    Default class constructor.
   */
  Shared_spin_lock() = default;
  /**
    Default class destructor.
   */
  virtual ~Shared_spin_lock() = default;

  /**
    Blocks until the lock is acquired in shared mode.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &acquire_shared();
  /**
    Blocks until the lock is acquired in exclusive mode.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &acquire_exclusive();
  /**
    Tries to acquire the lock in shared mode.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &try_shared();
  /**
    Tries to acquire the lock in exclusive mode.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &try_exclusive();
  /**
    Releases the previously granted shared acquisition request.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &release_shared();
  /**
    Releases the previously granted exclusive acquisition request.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &release_exclusive();
  /**
    Returns whether the lock is acquired for shared access by the invoking
    thread.

    @return true if the lock was acquired in shared mode by the invoking
            thread
   */
  bool is_shared_acquisition();
  /**
    Returns whether the lock is acquired for exclusive access by the
    invoking thread.

    @return true if the lock was acquired in exclusive mode by the invoking
            thread
   */
  bool is_exclusive_acquisition();

 private:
  /** The total amount of threads accessing in shared mode  */
  memory::Aligned_atomic<long> m_shared_access{0};
  /** Whether or not any thread is accessing in or waiting for exclusive mode */
  memory::Aligned_atomic<bool> m_exclusive_access{false};

  /**
    Tries to lock or waits for locking in shared mode and increases the
    thread-local lock acquisition shared counter.

    @param try_and_fail Whether or not to try to lock of wait for acquiring.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &try_or_spin_shared_lock(bool try_and_fail);
  /**
    Tries to lock or waits for locking in shared mode and increases the
    thread-local lock acquisition shared counter.

    @param try_and_fail Whether or not to try to lock of wait for acquiring.

    @return A reference to `this` object, for chaining purposes.
   */
  Shared_spin_lock &try_or_spin_exclusive_lock(bool try_and_fail);
  /**
    Tries to acquire in shared mode.

    @return `true` if the attempt to acquire the lock in shared mode was
            successful.
   */
  bool try_shared_lock();
  /**
    Tries to acquire in exclusive mode.

    @return `true` if the attempt to acquire the lock in exclusive mode was
            successful.
   */
  bool try_exclusive_lock();
  /**
    Blocks until the lock is acquired in shared mode.
   */
  void spin_shared_lock();
  /**
    Blocks until the lock is acquired in exclusive mode.
   */
  void spin_exclusive_lock();
  /**
    Returns the thread-local lock counter map.
   */
  static std::map<Shared_spin_lock *, long> &acquired_spins();
};
}  // namespace lock

#endif  // LOCK_SHARED_SPIN_LOCK_INCLUDED