YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/rw_semaphore.h
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
#ifndef YB_UTIL_RW_SEMAPHORE_H
33
#define YB_UTIL_RW_SEMAPHORE_H
34
35
#include <boost/smart_ptr/detail/yield_k.hpp>
36
#include <glog/logging.h>
37
38
#include "yb/gutil/atomicops.h"
39
#include "yb/gutil/port.h"
40
41
namespace yb {
42
43
// Read-Write semaphore. 32bit uint that contains the number of readers.
44
// When someone wants to write, tries to set the 32bit, and waits until
45
// the readers have finished. Readers are spinning while the write flag is set.
46
//
47
// This rw-semaphore makes no attempt at fairness, though it does avoid write
48
// starvation (no new readers may obtain the lock if a write is waiting).
49
//
50
// Given that this is currently based only on spinning (and not futex),
51
// it should only be used in cases where the lock is held for very short
52
// time intervals.
53
//
54
// If the semaphore is expected to always be released from the same thread
55
// that acquired it, use rw_spinlock instead.
56
//
57
// In order to support easier debugging of leaked locks, this class can track
58
// the stack trace of the last thread to lock it in write mode. To do so,
59
// uncomment the following define:
60
//   #define RW_SEMAPHORE_TRACK_HOLDER 1
61
// ... and then in gdb, print the contents of the semaphore, and you should
62
// see the collected stack trace.
63
class rw_semaphore {
64
#ifndef NDEBUG
65
  static constexpr int64_t kInvalidThreadId = -1;
66
#endif
67
 public:
68
3.17M
  rw_semaphore() : state_(0) {}
69
1.11M
  ~rw_semaphore() {}
70
71
1.08G
  void lock_shared() {
72
1.08G
    int loop_count = 0;
73
1.08G
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
74
1.35G
    while (
true1.35G
) {
75
1.35G
      Atomic32 expected = cur_state & kNumReadersMask;   // I expect no write lock
76
1.35G
      Atomic32 try_new_state = expected + 1;          // Add me as reader
77
1.35G
      cur_state = base::subtle::Acquire_CompareAndSwap(&state_, expected, try_new_state);
78
1.35G
      if (cur_state == expected)
79
1.08G
        break;
80
      // Either was already locked by someone else, or CAS failed.
81
264M
      boost::detail::yield(loop_count++);
82
264M
    }
83
1.08G
  }
84
85
1.08G
  void unlock_shared() {
86
1.08G
    int loop_count = 0;
87
1.08G
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
88
1.08G
    while (
true1.08G
) {
89
1.08G
      DCHECK_GT(cur_state & kNumReadersMask, 0)
90
0
        << "unlock_shared() called when there are no shared locks held";
91
1.08G
      Atomic32 expected = cur_state;           // I expect a write lock and other readers
92
1.08G
      Atomic32 try_new_state = expected - 1;   // Drop me as reader
93
1.08G
      cur_state = base::subtle::Release_CompareAndSwap(&state_, expected, try_new_state);
94
1.08G
      if (cur_state == expected)
95
1.08G
        break;
96
      // Either was already locked by someone else, or CAS failed.
97
442k
      boost::detail::yield(loop_count++);
98
442k
    }
99
1.08G
  }
100
101
  // Tries to acquire a write lock, if no one else has it.
102
  // This function retries on CAS failure and waits for readers to complete.
103
0
  bool try_lock() {
104
0
    int loop_count = 0;
105
0
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
106
0
    while (true) {
107
0
      // someone else has already the write lock
108
0
      if (cur_state & kWriteFlag)
109
0
        return false;
110
0
111
0
      Atomic32 expected = cur_state & kNumReadersMask;   // I expect some 0+ readers
112
0
      Atomic32 try_new_state = kWriteFlag | expected;    // I want to lock the other writers
113
0
      cur_state = base::subtle::Acquire_CompareAndSwap(&state_, expected, try_new_state);
114
0
      if (cur_state == expected)
115
0
        break;
116
0
      // Either was already locked by someone else, or CAS failed.
117
0
      boost::detail::yield(loop_count++);
118
0
    }
119
0
120
0
    WaitPendingReaders();
121
0
    RecordLockHolderStack();
122
0
    return true;
123
0
  }
124
125
75.7M
  void lock() {
126
75.7M
    int loop_count = 0;
127
75.7M
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
128
83.7M
    while (
true83.7M
) {
129
83.7M
      Atomic32 expected = cur_state & kNumReadersMask;   // I expect some 0+ readers
130
83.7M
      Atomic32 try_new_state = kWriteFlag | expected;    // I want to lock the other writers
131
      // Note: we use NoBarrier here because we'll do the Acquire barrier down below
132
      // in WaitPendingReaders
133
83.7M
      cur_state = base::subtle::NoBarrier_CompareAndSwap(&state_, expected, try_new_state);
134
83.7M
      if (cur_state == expected)
135
75.7M
        break;
136
      // Either was already locked by someone else, or CAS failed.
137
7.96M
      boost::detail::yield(loop_count++);
138
7.96M
    }
139
140
75.7M
    WaitPendingReaders();
141
142
75.7M
#ifndef NDEBUG
143
75.7M
    AssignWriterTid();
144
75.7M
#endif // NDEBUG
145
75.7M
    RecordLockHolderStack();
146
75.7M
  }
147
148
75.7M
  void unlock() {
149
    // I expect to be the only writer
150
75.7M
    DCHECK_EQ(base::subtle::NoBarrier_Load(&state_), kWriteFlag);
151
152
75.7M
#ifndef NDEBUG
153
75.7M
    writer_tid_ = kInvalidThreadId; // Invalid tid.
154
75.7M
#endif // NDEBUG
155
156
75.7M
    ResetLockHolderStack();
157
    // Reset: no writers & no readers.
158
75.7M
    Release_Store(&state_, 0);
159
75.7M
  }
160
161
  // Return true if the lock is currently held for write by any thread.
162
  // See simple_semaphore::is_locked() for details about where this is useful.
163
0
  bool is_write_locked() const {
164
0
    return base::subtle::NoBarrier_Load(&state_) & kWriteFlag;
165
0
  }
166
167
  // Return true if the lock is currently held, either for read or write
168
  // by any thread.
169
  // See simple_semaphore::is_locked() for details about where this is useful.
170
3
  bool is_locked() const {
171
3
    return base::subtle::NoBarrier_Load(&state_);
172
3
  }
173
174
 private:
175
  static const uint32_t kNumReadersMask = 0x7fffffff;
176
  static const uint32_t kWriteFlag = 1 << 31;
177
178
#ifdef RW_SEMAPHORE_TRACK_HOLDER
179
  StackTrace writer_stack_;
180
  void RecordLockHolderStack() {
181
    writer_stack_.Collect();
182
  }
183
  void ResetLockHolderStack() {
184
    writer_stack_.Reset();
185
  }
186
#else
187
75.7M
  void RecordLockHolderStack() {
188
75.7M
  }
189
75.7M
  void ResetLockHolderStack() {
190
75.7M
  }
191
#endif
192
193
75.7M
  void WaitPendingReaders() {
194
75.7M
    int loop_count = 0;
195
75.8M
    while ((base::subtle::Acquire_Load(&state_) & kNumReadersMask) > 0) {
196
103k
      boost::detail::yield(loop_count++);
197
103k
    }
198
75.7M
  }
199
200
 private:
201
  volatile Atomic32 state_;
202
#ifndef NDEBUG
203
  void AssignWriterTid();
204
205
  int64_t writer_tid_ = kInvalidThreadId;
206
#endif // NDEBUG
207
};
208
209
} // namespace yb
210
#endif /* YB_UTIL_RW_SEMAPHORE_H */