Apollo  6.0
Open source self driving car software
reentrant_rw_lock.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2018 The Apollo Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *****************************************************************************/
16 
17 #ifndef CYBER_BASE_REENTRANT_RW_LOCK_H_
18 #define CYBER_BASE_REENTRANT_RW_LOCK_H_
19 
20 #include <unistd.h>
21 
22 #include <atomic>
23 #include <condition_variable>
24 #include <cstdint>
25 #include <cstdlib>
26 #include <iostream>
27 #include <mutex>
28 #include <thread>
29 
31 
32 namespace apollo {
33 namespace cyber {
34 namespace base {
35 
36 static const std::thread::id NULL_THREAD_ID = std::thread::id();
40 
41  public:
42  static const int32_t RW_LOCK_FREE = 0;
43  static const int32_t WRITE_EXCLUSIVE = -1;
44  static const uint32_t MAX_RETRY_TIMES = 5;
45  static const std::thread::id null_thread;
47  explicit ReentrantRWLock(bool write_first) : write_first_(write_first) {}
48 
49  private:
50  // all these function only can used by ReadLockGuard/WriteLockGuard;
51  void ReadLock();
52  void WriteLock();
53 
54  void ReadUnlock();
55  void WriteUnlock();
56 
57  ReentrantRWLock(const ReentrantRWLock&) = delete;
58  ReentrantRWLock& operator=(const ReentrantRWLock&) = delete;
59  std::thread::id write_thread_id_ = {NULL_THREAD_ID};
60  std::atomic<uint32_t> write_lock_wait_num_ = {0};
61  std::atomic<int32_t> lock_num_ = {0};
62  bool write_first_ = true;
63 };
64 
65 inline void ReentrantRWLock::ReadLock() {
66  if (write_thread_id_ == std::this_thread::get_id()) {
67  return;
68  }
69 
70  uint32_t retry_times = 0;
71  int32_t lock_num = lock_num_.load(std::memory_order_acquire);
72  if (write_first_) {
73  do {
74  while (lock_num < RW_LOCK_FREE ||
75  write_lock_wait_num_.load(std::memory_order_acquire) > 0) {
76  if (++retry_times == MAX_RETRY_TIMES) {
77  // saving cpu
78  std::this_thread::yield();
79  retry_times = 0;
80  }
81  lock_num = lock_num_.load(std::memory_order_acquire);
82  }
83  } while (!lock_num_.compare_exchange_weak(lock_num, lock_num + 1,
84  std::memory_order_acq_rel,
85  std::memory_order_relaxed));
86  } else {
87  do {
88  while (lock_num < RW_LOCK_FREE) {
89  if (++retry_times == MAX_RETRY_TIMES) {
90  // saving cpu
91  std::this_thread::yield();
92  retry_times = 0;
93  }
94  lock_num = lock_num_.load(std::memory_order_acquire);
95  }
96  } while (!lock_num_.compare_exchange_weak(lock_num, lock_num + 1,
97  std::memory_order_acq_rel,
98  std::memory_order_relaxed));
99  }
100 }
101 
102 inline void ReentrantRWLock::WriteLock() {
103  auto this_thread_id = std::this_thread::get_id();
104  if (write_thread_id_ == this_thread_id) {
105  lock_num_.fetch_sub(1);
106  return;
107  }
108  int32_t rw_lock_free = RW_LOCK_FREE;
109  uint32_t retry_times = 0;
110  write_lock_wait_num_.fetch_add(1);
111  while (!lock_num_.compare_exchange_weak(rw_lock_free, WRITE_EXCLUSIVE,
112  std::memory_order_acq_rel,
113  std::memory_order_relaxed)) {
114  // rw_lock_free will change after CAS fail, so init agin
115  rw_lock_free = RW_LOCK_FREE;
116  if (++retry_times == MAX_RETRY_TIMES) {
117  // saving cpu
118  std::this_thread::yield();
119  retry_times = 0;
120  }
121  }
122  write_thread_id_ = this_thread_id;
123  write_lock_wait_num_.fetch_sub(1);
124 }
125 
126 inline void ReentrantRWLock::ReadUnlock() {
127  if (write_thread_id_ == std::this_thread::get_id()) {
128  return;
129  }
130  lock_num_.fetch_sub(1);
131 }
132 
133 inline void ReentrantRWLock::WriteUnlock() {
134  if (lock_num_.fetch_add(1) == WRITE_EXCLUSIVE) {
135  write_thread_id_ = NULL_THREAD_ID;
136  }
137 }
138 
139 } // namespace base
140 } // namespace cyber
141 } // namespace apollo
142 
143 #endif // CYBER_BASE_REENTRANT_RW_LOCK_H_
static const uint32_t MAX_RETRY_TIMES
Definition: reentrant_rw_lock.h:44
ReentrantRWLock()
Definition: reentrant_rw_lock.h:46
PlanningContext is the runtime context in planning. It is persistent across multiple frames...
Definition: atomic_hash_map.h:25
Definition: rw_lock_guard.h:48
ReentrantRWLock(bool write_first)
Definition: reentrant_rw_lock.h:47
static const int32_t WRITE_EXCLUSIVE
Definition: reentrant_rw_lock.h:43
Definition: rw_lock_guard.h:35
static const int32_t RW_LOCK_FREE
Definition: reentrant_rw_lock.h:42
Definition: reentrant_rw_lock.h:37
static const std::thread::id null_thread
Definition: reentrant_rw_lock.h:45