From b9dbc7b1f7cf3e6994686701f185daa71cab69bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= <jerome.duval@gmail.com>
Date: Tue, 30 May 2017 22:30:06 +0200
Subject: [PATCH] pthread_rwlock: draft local implementation.
---
headers/posix/sys/types.h | 9 +-
.../libroot/posix/pthread/pthread_rwlock.cpp | 190 +++++++++------------
2 files changed, 87 insertions(+), 112 deletions(-)
diff --git a/headers/posix/sys/types.h b/headers/posix/sys/types.h
index b09aba5..745c1fa 100644
a
|
b
|
struct _pthread_rwlock {
|
108 | 108 | __haiku_std_int32 sem; |
109 | 109 | } shared; |
110 | 110 | struct { |
111 | | __haiku_std_int32 lock_sem; |
112 | | __haiku_std_int32 lock_count; |
| 111 | __haiku_std_int32 unused; |
| 112 | __haiku_std_int32 mutex; |
113 | 113 | __haiku_std_int32 reader_count; |
114 | 114 | __haiku_std_int32 writer_count; |
115 | | void* waiters[2]; |
| 115 | __haiku_std_int32 write_waiter_count; |
| 116 | __haiku_std_int32 write_lock; |
| 117 | __haiku_std_int32 read_waiter_count; |
| 118 | __haiku_std_int32 read_lock; |
116 | 119 | } local; |
117 | 120 | } u; |
118 | 121 | }; |
diff --git a/src/system/libroot/posix/pthread/pthread_rwlock.cpp b/src/system/libroot/posix/pthread/pthread_rwlock.cpp
index 65e83be..b8c667c 100644
a
|
b
|
|
16 | 16 | #include <util/DoublyLinkedList.h> |
17 | 17 | |
18 | 18 | #include "pthread_private.h" |
| 19 | #include <user_mutex_defs.h> |
19 | 20 | |
20 | 21 | |
21 | 22 | #define MAX_READER_COUNT 1000000 |
… |
… |
struct SharedRWLock {
|
93 | 94 | struct LocalRWLock { |
94 | 95 | uint32_t flags; |
95 | 96 | int32_t owner; |
96 | | int32_t lock_sem; |
97 | | int32_t lock_count; |
| 97 | int32_t unused; |
| 98 | int32_t mutex; |
98 | 99 | int32_t reader_count; |
99 | 100 | int32_t writer_count; |
100 | | // Note, that reader_count and writer_count are not used the same way. |
101 | | // writer_count includes the write lock owner as well as waiting |
102 | | // writers. reader_count includes read lock owners only. |
103 | | WaiterList waiters; |
| 101 | int32_t write_waiter_count; |
| 102 | int32_t write_lock; |
| 103 | int32_t read_waiter_count; |
| 104 | int32_t read_lock; |
104 | 105 | |
105 | 106 | status_t Init() |
106 | 107 | { |
107 | 108 | flags = 0; |
108 | 109 | owner = -1; |
109 | | lock_sem = create_sem(0, "pthread rwlock"); |
110 | | lock_count = 1; |
| 110 | mutex = 0; |
111 | 111 | reader_count = 0; |
112 | 112 | writer_count = 0; |
113 | | new(&waiters) WaiterList; |
114 | | |
115 | | return lock_sem >= 0 ? B_OK : EAGAIN; |
| 113 | write_waiter_count = 0; |
| 114 | write_lock = 0; |
| 115 | read_waiter_count = 0; |
| 116 | read_lock = 0; |
| 117 | return B_OK; |
116 | 118 | } |
117 | 119 | |
118 | 120 | status_t Destroy() |
119 | 121 | { |
120 | | if (lock_sem < 0) |
121 | | return B_BAD_VALUE; |
122 | | return delete_sem(lock_sem) == B_OK ? B_OK : B_BAD_VALUE; |
| 122 | Locker locker(this); |
| 123 | if (reader_count > 0 || writer_count > 0 || read_waiter_count > 0 |
| 124 | || write_waiter_count > 0) |
| 125 | return EBUSY; |
| 126 | return 0; |
123 | 127 | } |
124 | 128 | |
125 | 129 | bool StructureLock() |
126 | 130 | { |
127 | | if (atomic_add((int32*)&lock_count, -1) <= 0) |
128 | | acquire_sem(lock_sem); |
| 131 | // Enter critical region: lock the mutex |
| 132 | int32 status = atomic_or((int32*)&mutex, B_USER_MUTEX_LOCKED); |
| 133 | |
| 134 | // If already locked, call the kernel |
| 135 | if (status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) { |
| 136 | do { |
| 137 | status = _kern_mutex_lock((int32*)&mutex, NULL, 0, 0); |
| 138 | } while (status == B_INTERRUPTED); |
| 139 | |
| 140 | if (status != B_OK) |
| 141 | return false; |
| 142 | } |
129 | 143 | return true; |
130 | 144 | } |
131 | 145 | |
132 | 146 | void StructureUnlock() |
133 | 147 | { |
134 | | if (atomic_add((int32*)&lock_count, 1) < 0) |
135 | | release_sem(lock_sem); |
| 148 | // Exit critical region: unlock the mutex |
| 149 | int32 status = atomic_and((int32*)&mutex, |
| 150 | ~(int32)B_USER_MUTEX_LOCKED); |
| 151 | |
| 152 | if (status & B_USER_MUTEX_WAITING) |
| 153 | _kern_mutex_unlock((int32*)&mutex, 0); |
136 | 154 | } |
137 | 155 | |
138 | 156 | status_t ReadLock(bigtime_t timeout) |
139 | 157 | { |
140 | 158 | Locker locker(this); |
141 | 159 | |
142 | | if (writer_count == 0) { |
143 | | reader_count++; |
144 | | return B_OK; |
| 160 | status_t status = B_OK; |
| 161 | if (writer_count > 0 || write_waiter_count > 0) { |
| 162 | read_waiter_count++; |
| 163 | do { |
| 164 | atomic_or((int32*)&read_lock, B_USER_MUTEX_LOCKED); |
| 165 | status = _kern_mutex_switch_lock((int32*)&mutex, |
| 166 | (int32*)&read_lock, "pthread rwlock", |
| 167 | timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, |
| 168 | timeout); |
| 169 | StructureLock(); |
| 170 | if (status != 0) |
| 171 | break; |
| 172 | } while (writer_count > 0 || write_waiter_count > 0); |
| 173 | read_waiter_count--; |
145 | 174 | } |
146 | 175 | |
147 | | return _Wait(false, timeout); |
| 176 | if (status == 0) |
| 177 | reader_count++; |
| 178 | |
| 179 | return status; |
148 | 180 | } |
149 | 181 | |
150 | 182 | status_t WriteLock(bigtime_t timeout) |
151 | 183 | { |
152 | 184 | Locker locker(this); |
153 | 185 | |
154 | | if (reader_count == 0 && writer_count == 0) { |
| 186 | status_t status = B_OK; |
| 187 | if (reader_count > 0 || writer_count > 0) { |
| 188 | write_waiter_count++; |
| 189 | do { |
| 190 | atomic_or((int32*)&write_lock, B_USER_MUTEX_LOCKED); |
| 191 | status = _kern_mutex_switch_lock((int32*)&mutex, |
| 192 | (int32*)&write_lock, "pthread rwlock", |
| 193 | timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, |
| 194 | timeout); |
| 195 | StructureLock(); |
| 196 | if (status != 0) |
| 197 | break; |
| 198 | } while (reader_count > 0 || writer_count > 0); |
| 199 | write_waiter_count--; |
| 200 | } |
| 201 | if (status == 0) { |
155 | 202 | writer_count++; |
156 | 203 | owner = find_thread(NULL); |
157 | | return B_OK; |
158 | 204 | } |
159 | | |
160 | | return _Wait(true, timeout); |
| 205 | return status; |
161 | 206 | } |
162 | 207 | |
163 | 208 | status_t Unlock() |
… |
… |
struct LocalRWLock {
|
167 | 212 | if (find_thread(NULL) == owner) { |
168 | 213 | writer_count--; |
169 | 214 | owner = -1; |
170 | | } else |
| 215 | if (write_waiter_count > 0) |
| 216 | _kern_mutex_unlock((int32*)&write_lock, 0); |
| 217 | else if (read_waiter_count > 0) |
| 218 | _kern_mutex_unlock((int32*)&read_lock, B_USER_MUTEX_UNBLOCK_ALL); |
| 219 | } else { |
171 | 220 | reader_count--; |
172 | | |
173 | | _Unblock(); |
| 221 | if (reader_count == 0 && write_waiter_count > 0) |
| 222 | _kern_mutex_unlock((int32*)&write_lock, 0); |
| 223 | else if (read_waiter_count > 0) |
| 224 | _kern_mutex_unlock((int32*)&read_lock, B_USER_MUTEX_UNBLOCK_ALL); |
| 225 | } |
174 | 226 | |
175 | 227 | return B_OK; |
176 | 228 | } |
177 | 229 | |
178 | 230 | private: |
179 | | status_t _Wait(bool writer, bigtime_t timeout) |
180 | | { |
181 | | if (timeout == 0) |
182 | | return B_TIMED_OUT; |
183 | | |
184 | | Waiter waiter(writer); |
185 | | waiters.Add(&waiter); |
186 | | waiter.queued = true; |
187 | | waiter.userThread->wait_status = 1; |
188 | | |
189 | | if (writer) |
190 | | writer_count++; |
191 | | |
192 | | StructureUnlock(); |
193 | | status_t error = _kern_block_thread( |
194 | | timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); |
195 | | StructureLock(); |
196 | | |
197 | | if (!waiter.queued) |
198 | | return waiter.status; |
199 | | |
200 | | // we're still queued, which means an error (timeout, interrupt) |
201 | | // occurred |
202 | | waiters.Remove(&waiter); |
203 | | |
204 | | if (writer) |
205 | | writer_count--; |
206 | | |
207 | | _Unblock(); |
208 | | |
209 | | return error; |
210 | | } |
211 | | |
212 | | void _Unblock() |
213 | | { |
214 | | // Check whether there any waiting threads at all and whether anyone |
215 | | // has the write lock |
216 | | Waiter* waiter = waiters.Head(); |
217 | | if (waiter == NULL || owner >= 0) |
218 | | return; |
219 | | |
220 | | // writer at head of queue? |
221 | | if (waiter->writer) { |
222 | | if (reader_count == 0) { |
223 | | waiter->status = B_OK; |
224 | | waiter->queued = false; |
225 | | waiters.Remove(waiter); |
226 | | owner = waiter->thread; |
227 | | |
228 | | if (waiter->userThread->wait_status > 0) |
229 | | _kern_unblock_thread(waiter->thread, B_OK); |
230 | | } |
231 | | return; |
232 | | } |
233 | | |
234 | | // wake up one or more readers -- we unblock more than one reader at |
235 | | // a time to save trips to the kernel |
236 | | while (!waiters.IsEmpty() && !waiters.Head()->writer) { |
237 | | static const int kMaxReaderUnblockCount = 128; |
238 | | thread_id readers[kMaxReaderUnblockCount]; |
239 | | int readerCount = 0; |
240 | | |
241 | | while (readerCount < kMaxReaderUnblockCount |
242 | | && (waiter = waiters.Head()) != NULL |
243 | | && !waiter->writer) { |
244 | | waiter->status = B_OK; |
245 | | waiter->queued = false; |
246 | | waiters.Remove(waiter); |
247 | | |
248 | | if (waiter->userThread->wait_status > 0) { |
249 | | readers[readerCount++] = waiter->thread; |
250 | | reader_count++; |
251 | | } |
252 | | } |
253 | | |
254 | | if (readerCount > 0) |
255 | | _kern_unblock_threads(readers, readerCount, B_OK); |
256 | | } |
257 | | } |
258 | | |
259 | 231 | |
260 | 232 | struct Locking { |
261 | 233 | inline bool Lock(LocalRWLock* lockable) |