headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / posix / pthread / pthread_rwlock.cpp
blobb2470aea086c8e7fd7c52a48f833db38f71dee13
1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <pthread.h>
8 #include <new>
10 #include <Debug.h>
12 #include <AutoLocker.h>
13 #include <libroot_lock.h>
14 #include <syscalls.h>
15 #include <user_mutex_defs.h>
16 #include <user_thread.h>
17 #include <util/DoublyLinkedList.h>
19 #include "pthread_private.h"
21 #define MAX_READER_COUNT 1000000
23 #define RWLOCK_FLAG_SHARED 0x01
26 struct Waiter : DoublyLinkedListLinkImpl<Waiter> {
27 Waiter(bool writer)
29 userThread(get_user_thread()),
30 thread(find_thread(NULL)),
31 writer(writer),
32 queued(false)
36 user_thread* userThread;
37 thread_id thread;
38 status_t status;
39 bool writer;
40 bool queued;
43 typedef DoublyLinkedList<Waiter> WaiterList;
46 struct SharedRWLock {
47 uint32_t flags;
48 int32_t owner;
49 int32_t sem;
51 status_t Init()
53 flags = RWLOCK_FLAG_SHARED;
54 owner = -1;
55 sem = create_sem(MAX_READER_COUNT, "pthread rwlock");
57 return sem >= 0 ? B_OK : EAGAIN;
60 status_t Destroy()
62 if (sem < 0)
63 return B_BAD_VALUE;
64 return delete_sem(sem) == B_OK ? B_OK : B_BAD_VALUE;
67 status_t ReadLock(bigtime_t timeout)
69 return acquire_sem_etc(sem, 1,
70 timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
73 status_t WriteLock(bigtime_t timeout)
75 status_t error = acquire_sem_etc(sem, MAX_READER_COUNT,
76 timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
77 if (error == B_OK)
78 owner = find_thread(NULL);
79 return error;
82 status_t Unlock()
84 if (find_thread(NULL) == owner) {
85 owner = -1;
86 return release_sem_etc(sem, MAX_READER_COUNT, 0);
87 } else
88 return release_sem(sem);
93 struct LocalRWLock {
94 uint32_t flags;
95 int32_t owner;
96 int32_t mutex;
97 int32_t unused;
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;
105 status_t Init()
107 flags = 0;
108 owner = -1;
109 mutex = 0;
110 reader_count = 0;
111 writer_count = 0;
112 new(&waiters) WaiterList;
114 return B_OK;
117 status_t Destroy()
119 Locker locker(this);
120 if (reader_count > 0 || waiters.Head() != NULL || writer_count > 0)
121 return EBUSY;
122 return B_OK;
125 bool StructureLock()
127 // Enter critical region: lock the mutex
128 int32 status = atomic_or((int32*)&mutex, B_USER_MUTEX_LOCKED);
130 // If already locked, call the kernel
131 if ((status & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) != 0) {
132 do {
133 status = _kern_mutex_lock((int32*)&mutex, NULL, 0, 0);
134 } while (status == B_INTERRUPTED);
136 if (status != B_OK)
137 return false;
139 return true;
142 void StructureUnlock()
144 // Exit critical region: unlock the mutex
145 int32 status = atomic_and((int32*)&mutex,
146 ~(int32)B_USER_MUTEX_LOCKED);
148 if ((status & B_USER_MUTEX_WAITING) != 0)
149 _kern_mutex_unlock((int32*)&mutex, 0);
152 status_t ReadLock(bigtime_t timeout)
154 Locker locker(this);
156 if (writer_count == 0) {
157 reader_count++;
158 return B_OK;
161 return _Wait(false, timeout);
164 status_t WriteLock(bigtime_t timeout)
166 Locker locker(this);
168 if (reader_count == 0 && writer_count == 0) {
169 writer_count++;
170 owner = find_thread(NULL);
171 return B_OK;
174 return _Wait(true, timeout);
177 status_t Unlock()
179 Locker locker(this);
181 if (find_thread(NULL) == owner) {
182 writer_count--;
183 owner = -1;
184 } else
185 reader_count--;
187 _Unblock();
189 return B_OK;
192 private:
193 status_t _Wait(bool writer, bigtime_t timeout)
195 if (timeout == 0)
196 return B_TIMED_OUT;
198 Waiter waiter(writer);
199 waiters.Add(&waiter);
200 waiter.queued = true;
201 waiter.userThread->wait_status = 1;
203 if (writer)
204 writer_count++;
206 StructureUnlock();
207 status_t error = _kern_block_thread(
208 timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
209 StructureLock();
211 if (!waiter.queued)
212 return waiter.status;
214 // we're still queued, which means an error (timeout, interrupt)
215 // occurred
216 waiters.Remove(&waiter);
218 if (writer)
219 writer_count--;
221 _Unblock();
223 return error;
226 void _Unblock()
228 // Check whether there any waiting threads at all and whether anyone
229 // has the write lock
230 Waiter* waiter = waiters.Head();
231 if (waiter == NULL || owner >= 0)
232 return;
234 // writer at head of queue?
235 if (waiter->writer) {
236 if (reader_count == 0) {
237 waiter->status = B_OK;
238 waiter->queued = false;
239 waiters.Remove(waiter);
240 owner = waiter->thread;
242 if (waiter->userThread->wait_status > 0)
243 _kern_unblock_thread(waiter->thread, B_OK);
245 return;
248 // wake up one or more readers -- we unblock more than one reader at
249 // a time to save trips to the kernel
250 while (!waiters.IsEmpty() && !waiters.Head()->writer) {
251 static const int kMaxReaderUnblockCount = 128;
252 thread_id readers[kMaxReaderUnblockCount];
253 int readerCount = 0;
255 while (readerCount < kMaxReaderUnblockCount
256 && (waiter = waiters.Head()) != NULL
257 && !waiter->writer) {
258 waiter->status = B_OK;
259 waiter->queued = false;
260 waiters.Remove(waiter);
262 if (waiter->userThread->wait_status > 0) {
263 readers[readerCount++] = waiter->thread;
264 reader_count++;
268 if (readerCount > 0)
269 _kern_unblock_threads(readers, readerCount, B_OK);
274 struct Locking {
275 inline bool Lock(LocalRWLock* lockable)
277 return lockable->StructureLock();
280 inline void Unlock(LocalRWLock* lockable)
282 lockable->StructureUnlock();
285 typedef AutoLocker<LocalRWLock, Locking> Locker;
289 static void inline
290 assert_dummy()
292 STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(SharedRWLock));
293 STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(LocalRWLock));
297 // #pragma mark - public lock functions
301 pthread_rwlock_init(pthread_rwlock_t* lock, const pthread_rwlockattr_t* _attr)
303 pthread_rwlockattr* attr = _attr != NULL ? *_attr : NULL;
304 bool shared = attr != NULL && (attr->flags & RWLOCK_FLAG_SHARED) != 0;
306 if (shared)
307 return ((SharedRWLock*)lock)->Init();
308 else
309 return ((LocalRWLock*)lock)->Init();
314 pthread_rwlock_destroy(pthread_rwlock_t* lock)
316 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
317 return ((SharedRWLock*)lock)->Destroy();
318 else
319 return ((LocalRWLock*)lock)->Destroy();
324 pthread_rwlock_rdlock(pthread_rwlock_t* lock)
326 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
327 return ((SharedRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT);
328 else
329 return ((LocalRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT);
334 pthread_rwlock_tryrdlock(pthread_rwlock_t* lock)
336 status_t error;
337 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
338 error = ((SharedRWLock*)lock)->ReadLock(0);
339 else
340 error = ((LocalRWLock*)lock)->ReadLock(0);
342 return error == B_TIMED_OUT ? EBUSY : error;
346 int pthread_rwlock_timedrdlock(pthread_rwlock_t* lock,
347 const struct timespec *timeout)
349 bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL
350 + timeout->tv_nsec / 1000LL;
352 status_t error;
353 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
354 error = ((SharedRWLock*)lock)->ReadLock(timeoutMicros);
355 else
356 error = ((LocalRWLock*)lock)->ReadLock(timeoutMicros);
358 return error == B_TIMED_OUT ? EBUSY : error;
363 pthread_rwlock_wrlock(pthread_rwlock_t* lock)
365 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
366 return ((SharedRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT);
367 else
368 return ((LocalRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT);
373 pthread_rwlock_trywrlock(pthread_rwlock_t* lock)
375 status_t error;
376 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
377 error = ((SharedRWLock*)lock)->WriteLock(0);
378 else
379 error = ((LocalRWLock*)lock)->WriteLock(0);
381 return error == B_TIMED_OUT ? EBUSY : error;
386 pthread_rwlock_timedwrlock(pthread_rwlock_t* lock,
387 const struct timespec *timeout)
389 bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL
390 + timeout->tv_nsec / 1000LL;
392 status_t error;
393 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
394 error = ((SharedRWLock*)lock)->WriteLock(timeoutMicros);
395 else
396 error = ((LocalRWLock*)lock)->WriteLock(timeoutMicros);
398 return error == B_TIMED_OUT ? EBUSY : error;
403 pthread_rwlock_unlock(pthread_rwlock_t* lock)
405 if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
406 return ((SharedRWLock*)lock)->Unlock();
407 else
408 return ((LocalRWLock*)lock)->Unlock();
412 // #pragma mark - public attribute functions
416 pthread_rwlockattr_init(pthread_rwlockattr_t* _attr)
418 pthread_rwlockattr* attr = (pthread_rwlockattr*)malloc(
419 sizeof(pthread_rwlockattr));
420 if (attr == NULL)
421 return B_NO_MEMORY;
423 attr->flags = 0;
424 *_attr = attr;
426 return 0;
431 pthread_rwlockattr_destroy(pthread_rwlockattr_t* _attr)
433 pthread_rwlockattr* attr = *_attr;
435 free(attr);
436 return 0;
441 pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* _attr, int* shared)
443 pthread_rwlockattr* attr = *_attr;
445 *shared = (attr->flags & RWLOCK_FLAG_SHARED) != 0
446 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
447 return 0;
452 pthread_rwlockattr_setpshared(pthread_rwlockattr_t* _attr, int shared)
454 pthread_rwlockattr* attr = *_attr;
456 if (shared == PTHREAD_PROCESS_SHARED)
457 attr->flags |= RWLOCK_FLAG_SHARED;
458 else
459 attr->flags &= ~RWLOCK_FLAG_SHARED;
461 return 0;