From 17ec186672d5184fa89bf4360b7922c1ce4ca6e3 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: local implementation with mutexes.
* add PTHREAD_RWLOCK_INITIALIZER macro to statically initialize.
---
headers/posix/pthread.h | 2 +
headers/posix/sys/types.h | 12 +-
.../libroot/posix/pthread/pthread_rwlock.cpp | 202 +++++++++------------
3 files changed, 99 insertions(+), 117 deletions(-)
diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h
index 489aace..e55ee62 100644
a
|
b
|
extern "C" {
|
80 | 80 | { PTHREAD_MUTEX_RECURSIVE, 0, -42, -1, 0 } |
81 | 81 | #define PTHREAD_COND_INITIALIZER \ |
82 | 82 | { 0, -42, NULL, 0, 0 } |
| 83 | #define PTHREAD_RWLOCK_INITIALIZER \ |
| 84 | { 0, -1, 0, 0, 0, 0, 0, 0 } |
83 | 85 | |
84 | 86 | /* mutex functions */ |
85 | 87 | extern int pthread_mutex_destroy(pthread_mutex_t *mutex); |
diff --git a/headers/posix/sys/types.h b/headers/posix/sys/types.h
index b09aba5..4fb7ec8 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 mutex; |
113 | 112 | __haiku_std_int32 reader_count; |
114 | | __haiku_std_int32 writer_count; |
115 | | void* waiters[2]; |
| 113 | __haiku_std_int32 write_waiter_count; |
| 114 | __haiku_std_int32 write_lock; |
| 115 | __haiku_std_int32 read_waiter_count; |
| 116 | __haiku_std_int32 read_lock; |
| 117 | #ifdef B_HAIKU_64_BIT |
| 118 | __haiku_std_int32 unused[2]; |
| 119 | #endif |
116 | 120 | } local; |
117 | 121 | } u; |
118 | 122 | }; |
diff --git a/src/system/libroot/posix/pthread/pthread_rwlock.cpp b/src/system/libroot/posix/pthread/pthread_rwlock.cpp
index 65e83be..0243100 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 mutex; |
98 | 98 | int32_t reader_count; |
99 | | 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; |
| 99 | int32_t write_waiter_count; |
| 100 | int32_t write_lock; |
| 101 | int32_t read_waiter_count; |
| 102 | int32_t read_lock; |
104 | 103 | |
105 | 104 | status_t Init() |
106 | 105 | { |
107 | 106 | flags = 0; |
108 | 107 | owner = -1; |
109 | | lock_sem = create_sem(0, "pthread rwlock"); |
110 | | lock_count = 1; |
| 108 | mutex = 0; |
111 | 109 | reader_count = 0; |
112 | | writer_count = 0; |
113 | | new(&waiters) WaiterList; |
114 | | |
115 | | return lock_sem >= 0 ? B_OK : EAGAIN; |
| 110 | write_waiter_count = 0; |
| 111 | write_lock = 0; |
| 112 | read_waiter_count = 0; |
| 113 | read_lock = 0; |
| 114 | return B_OK; |
116 | 115 | } |
117 | 116 | |
118 | 117 | status_t Destroy() |
119 | 118 | { |
120 | | if (lock_sem < 0) |
121 | | return B_BAD_VALUE; |
122 | | return delete_sem(lock_sem) == B_OK ? B_OK : B_BAD_VALUE; |
| 119 | Locker locker(this); |
| 120 | if (reader_count > 0 || owner != -1 || read_waiter_count > 0 |
| 121 | || write_waiter_count > 0) |
| 122 | return EBUSY; |
| 123 | return 0; |
123 | 124 | } |
124 | 125 | |
125 | 126 | bool StructureLock() |
126 | 127 | { |
127 | | if (atomic_add((int32*)&lock_count, -1) <= 0) |
128 | | acquire_sem(lock_sem); |
| 128 | // Enter critical region: lock the mutex |
| 129 | int32 status = atomic_or((int32*)&mutex, B_USER_MUTEX_LOCKED); |
| 130 | |
| 131 | // If already locked, call the kernel |
| 132 | if ((status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) != 0) { |
| 133 | do { |
| 134 | status = _kern_mutex_lock((int32*)&mutex, NULL, 0, 0); |
| 135 | } while (status == B_INTERRUPTED); |
| 136 | |
| 137 | if (status != B_OK) |
| 138 | return false; |
| 139 | } |
129 | 140 | return true; |
130 | 141 | } |
131 | 142 | |
132 | 143 | void StructureUnlock() |
133 | 144 | { |
134 | | if (atomic_add((int32*)&lock_count, 1) < 0) |
135 | | release_sem(lock_sem); |
| 145 | // Exit critical region: unlock the mutex |
| 146 | int32 status = atomic_and((int32*)&mutex, |
| 147 | ~(int32)B_USER_MUTEX_LOCKED); |
| 148 | |
| 149 | if ((status & B_USER_MUTEX_WAITING) != 0) |
| 150 | _kern_mutex_unlock((int32*)&mutex, 0); |
136 | 151 | } |
137 | 152 | |
138 | 153 | status_t ReadLock(bigtime_t timeout) |
139 | 154 | { |
140 | 155 | Locker locker(this); |
141 | 156 | |
142 | | if (writer_count == 0) { |
143 | | reader_count++; |
144 | | return B_OK; |
| 157 | status_t status = B_OK; |
| 158 | if (owner != -1 || write_waiter_count > 0) { |
| 159 | if (timeout == 0) |
| 160 | return B_TIMED_OUT; |
| 161 | read_waiter_count++; |
| 162 | do { |
| 163 | atomic_or((int32*)&read_lock, B_USER_MUTEX_LOCKED); |
| 164 | status = _kern_mutex_switch_lock((int32*)&mutex, |
| 165 | (int32*)&read_lock, "pthread rwlock", |
| 166 | timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, |
| 167 | timeout); |
| 168 | StructureLock(); |
| 169 | if (status != 0) |
| 170 | break; |
| 171 | } while (owner != -1 || write_waiter_count > 0); |
| 172 | read_waiter_count--; |
145 | 173 | } |
146 | 174 | |
147 | | return _Wait(false, timeout); |
| 175 | if (status == 0) |
| 176 | reader_count++; |
| 177 | |
| 178 | return status; |
148 | 179 | } |
149 | 180 | |
150 | 181 | status_t WriteLock(bigtime_t timeout) |
151 | 182 | { |
152 | 183 | Locker locker(this); |
153 | 184 | |
154 | | if (reader_count == 0 && writer_count == 0) { |
155 | | writer_count++; |
| 185 | status_t status = B_OK; |
| 186 | if (reader_count > 0 || owner != -1) { |
| 187 | if (timeout == 0) |
| 188 | return B_TIMED_OUT; |
| 189 | write_waiter_count++; |
| 190 | do { |
| 191 | atomic_or((int32*)&write_lock, B_USER_MUTEX_LOCKED); |
| 192 | status = _kern_mutex_switch_lock((int32*)&mutex, |
| 193 | (int32*)&write_lock, "pthread rwlock", |
| 194 | timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, |
| 195 | timeout); |
| 196 | StructureLock(); |
| 197 | if (status != 0) |
| 198 | break; |
| 199 | } while (reader_count > 0 || owner != -1); |
| 200 | write_waiter_count--; |
| 201 | if (status == B_TIMED_OUT && write_waiter_count == 0 |
| 202 | && read_waiter_count > 0) { |
| 203 | _kern_mutex_unlock((int32*)&read_lock, |
| 204 | B_USER_MUTEX_UNBLOCK_ALL); |
| 205 | } |
| 206 | } |
| 207 | if (status == 0) { |
156 | 208 | owner = find_thread(NULL); |
157 | | return B_OK; |
158 | 209 | } |
159 | | |
160 | | return _Wait(true, timeout); |
| 210 | return status; |
161 | 211 | } |
162 | 212 | |
163 | 213 | status_t Unlock() |
… |
… |
struct LocalRWLock {
|
165 | 215 | Locker locker(this); |
166 | 216 | |
167 | 217 | if (find_thread(NULL) == owner) { |
168 | | writer_count--; |
169 | 218 | owner = -1; |
170 | | } else |
| 219 | if (write_waiter_count > 0) |
| 220 | _kern_mutex_unlock((int32*)&write_lock, 0); |
| 221 | else if (read_waiter_count > 0) |
| 222 | _kern_mutex_unlock((int32*)&read_lock, B_USER_MUTEX_UNBLOCK_ALL); |
| 223 | } else { |
171 | 224 | reader_count--; |
172 | | |
173 | | _Unblock(); |
| 225 | if (reader_count == 0 && write_waiter_count > 0) |
| 226 | _kern_mutex_unlock((int32*)&write_lock, 0); |
| 227 | else if (read_waiter_count > 0) |
| 228 | _kern_mutex_unlock((int32*)&read_lock, B_USER_MUTEX_UNBLOCK_ALL); |
| 229 | } |
174 | 230 | |
175 | 231 | return B_OK; |
176 | 232 | } |
177 | 233 | |
178 | 234 | 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 | 235 | |
260 | 236 | struct Locking { |
261 | 237 | inline bool Lock(LocalRWLock* lockable) |