YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
1.92M
  rw_semaphore() : state_(0) {}
69
753k
  ~rw_semaphore() {}
70
71
220M
  void lock_shared() {
72
220M
    int loop_count = 0;
73
220M
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
74
331M
    while (true) {
75
331M
      Atomic32 expected = cur_state & kNumReadersMask;   // I expect no write lock
76
331M
      Atomic32 try_new_state = expected + 1;          // Add me as reader
77
331M
      cur_state = base::subtle::Acquire_CompareAndSwap(&state_, expected, try_new_state);
78
331M
      if (cur_state == expected)
79
220M
        break;
80
      // Either was already locked by someone else, or CAS failed.
81
111M
      boost::detail::yield(loop_count++);
82
111M
    }
83
220M
  }
84
85
220M
  void unlock_shared() {
86
220M
    int loop_count = 0;
87
220M
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
88
220M
    while (true) {
89
0
      DCHECK_GT(cur_state & kNumReadersMask, 0)
90
0
        << "unlock_shared() called when there are no shared locks held";
91
220M
      Atomic32 expected = cur_state;           // I expect a write lock and other readers
92
220M
      Atomic32 try_new_state = expected - 1;   // Drop me as reader
93
220M
      cur_state = base::subtle::Release_CompareAndSwap(&state_, expected, try_new_state);
94
220M
      if (cur_state == expected)
95
220M
        break;
96
      // Either was already locked by someone else, or CAS failed.
97
209k
      boost::detail::yield(loop_count++);
98
209k
    }
99
220M
  }
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
31.4M
  void lock() {
126
31.4M
    int loop_count = 0;
127
31.4M
    Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_);
128
36.2M
    while (true) {
129
36.2M
      Atomic32 expected = cur_state & kNumReadersMask;   // I expect some 0+ readers
130
36.2M
      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
36.2M
      cur_state = base::subtle::NoBarrier_CompareAndSwap(&state_, expected, try_new_state);
134
36.2M
      if (cur_state == expected)
135
31.4M
        break;
136
      // Either was already locked by someone else, or CAS failed.
137
4.83M
      boost::detail::yield(loop_count++);
138
4.83M
    }
139
140
31.4M
    WaitPendingReaders();
141
142
31.4M
#ifndef NDEBUG
143
31.4M
    AssignWriterTid();
144
31.4M
#endif // NDEBUG
145
31.4M
    RecordLockHolderStack();
146
31.4M
  }
147
148
31.4M
  void unlock() {
149
    // I expect to be the only writer
150
31.4M
    DCHECK_EQ(base::subtle::NoBarrier_Load(&state_), kWriteFlag);
151
152
31.4M
#ifndef NDEBUG
153
31.4M
    writer_tid_ = kInvalidThreadId; // Invalid tid.
154
31.4M
#endif // NDEBUG
155
156
31.4M
    ResetLockHolderStack();
157
    // Reset: no writers & no readers.
158
31.4M
    Release_Store(&state_, 0);
159
31.4M
  }
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
4
  bool is_locked() const {
171
4
    return base::subtle::NoBarrier_Load(&state_);
172
4
  }
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
31.4M
  void RecordLockHolderStack() {
188
31.4M
  }
189
31.4M
  void ResetLockHolderStack() {
190
31.4M
  }
191
#endif
192
193
31.4M
  void WaitPendingReaders() {
194
31.4M
    int loop_count = 0;
195
31.4M
    while ((base::subtle::Acquire_Load(&state_) & kNumReadersMask) > 0) {
196
36.4k
      boost::detail::yield(loop_count++);
197
36.4k
    }
198
31.4M
  }
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 */