btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / shared / RWLockManager.cpp
blobfe306c5f3ef2ba4f77dcb595bc5a635d54fe4675
1 /*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <RWLockManager.h>
8 #include <AutoLocker.h>
10 #include <syscalls.h>
11 #include <user_thread.h>
14 RWLockable::RWLockable()
16 fOwner(-1),
17 fOwnerCount(0),
18 fReaderCount(0)
23 RWLockManager::RWLockManager()
25 fLock("r/w lock manager")
30 RWLockManager::~RWLockManager()
35 bool
36 RWLockManager::ReadLock(RWLockable* lockable)
38 AutoLocker<RWLockManager> locker(this);
40 if (lockable->fWaiters.IsEmpty()) {
41 lockable->fReaderCount++;
42 return true;
45 return _Wait(lockable, false, B_INFINITE_TIMEOUT) == B_OK;
49 bool
50 RWLockManager::TryReadLock(RWLockable* lockable)
52 AutoLocker<RWLockManager> locker(this);
54 if (lockable->fWaiters.IsEmpty()) {
55 lockable->fReaderCount++;
56 return true;
59 return false;
63 status_t
64 RWLockManager::ReadLockWithTimeout(RWLockable* lockable, bigtime_t timeout)
66 AutoLocker<RWLockManager> locker(this);
68 if (lockable->fWaiters.IsEmpty()) {
69 lockable->fReaderCount++;
70 return B_OK;
73 return _Wait(lockable, false, timeout);
77 void
78 RWLockManager::ReadUnlock(RWLockable* lockable)
80 AutoLocker<RWLockManager> locker(this);
82 if (lockable->fReaderCount <= 0) {
83 debugger("RWLockManager::ReadUnlock(): Not read-locked!");
84 return;
87 if (--lockable->fReaderCount == 0)
88 _Unblock(lockable);
92 bool
93 RWLockManager::WriteLock(RWLockable* lockable)
95 AutoLocker<RWLockManager> locker(this);
97 thread_id thread = find_thread(NULL);
99 if (lockable->fOwner == thread) {
100 lockable->fOwnerCount++;
101 return true;
104 if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
105 lockable->fOwnerCount = 1;
106 lockable->fOwner = find_thread(NULL);
107 return true;
110 return _Wait(lockable, true, B_INFINITE_TIMEOUT) == B_OK;
114 bool
115 RWLockManager::TryWriteLock(RWLockable* lockable)
117 AutoLocker<RWLockManager> locker(this);
119 thread_id thread = find_thread(NULL);
121 if (lockable->fOwner == thread) {
122 lockable->fOwnerCount++;
123 return true;
126 if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
127 lockable->fOwnerCount++;
128 lockable->fOwner = thread;
129 return true;
132 return false;
136 status_t
137 RWLockManager::WriteLockWithTimeout(RWLockable* lockable, bigtime_t timeout)
139 AutoLocker<RWLockManager> locker(this);
141 thread_id thread = find_thread(NULL);
143 if (lockable->fOwner == thread) {
144 lockable->fOwnerCount++;
145 return B_OK;
148 if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
149 lockable->fOwnerCount++;
150 lockable->fOwner = thread;
151 return B_OK;
154 return _Wait(lockable, true, timeout);
158 void
159 RWLockManager::WriteUnlock(RWLockable* lockable)
161 AutoLocker<RWLockManager> locker(this);
163 if (find_thread(NULL) != lockable->fOwner) {
164 debugger("RWLockManager::WriteUnlock(): Not write-locked by calling "
165 "thread!");
166 return;
169 if (--lockable->fOwnerCount == 0) {
170 lockable->fOwner = -1;
171 _Unblock(lockable);
176 status_t
177 RWLockManager::_Wait(RWLockable* lockable, bool writer, bigtime_t timeout)
179 if (timeout == 0)
180 return B_TIMED_OUT;
182 // enqueue a waiter
183 RWLockable::Waiter waiter(writer);
184 lockable->fWaiters.Add(&waiter);
185 waiter.queued = true;
186 get_user_thread()->wait_status = 1;
188 // wait
189 Unlock();
191 status_t error;
192 do {
193 error = _kern_block_thread(
194 timeout >= 0 ? B_RELATIVE_TIMEOUT : 0, timeout);
195 // TODO: When interrupted we should adjust the timeout, respectively
196 // convert to an absolute timeout in the first place!
197 } while (error == B_INTERRUPTED);
199 Lock();
201 if (!waiter.queued)
202 return waiter.status;
204 // we're still queued, which means an error (timeout, interrupt)
205 // occurred
206 lockable->fWaiters.Remove(&waiter);
208 _Unblock(lockable);
210 return error;
214 void
215 RWLockManager::_Unblock(RWLockable* lockable)
217 // Check whether there any waiting threads at all and whether anyone
218 // has the write lock
219 RWLockable::Waiter* waiter = lockable->fWaiters.Head();
220 if (waiter == NULL || lockable->fOwner >= 0)
221 return;
223 // writer at head of queue?
224 if (waiter->writer) {
225 if (lockable->fReaderCount == 0) {
226 waiter->status = B_OK;
227 waiter->queued = false;
228 lockable->fWaiters.Remove(waiter);
229 lockable->fOwner = waiter->thread;
230 lockable->fOwnerCount = 1;
232 _kern_unblock_thread(waiter->thread, B_OK);
234 return;
237 // wake up one or more readers -- we unblock more than one reader at
238 // a time to save trips to the kernel
239 while (!lockable->fWaiters.IsEmpty()
240 && !lockable->fWaiters.Head()->writer) {
241 static const int kMaxReaderUnblockCount = 128;
242 thread_id readers[kMaxReaderUnblockCount];
243 int readerCount = 0;
245 while (readerCount < kMaxReaderUnblockCount
246 && (waiter = lockable->fWaiters.Head()) != NULL
247 && !waiter->writer) {
248 waiter->status = B_OK;
249 waiter->queued = false;
250 lockable->fWaiters.Remove(waiter);
252 readers[readerCount++] = waiter->thread;
253 lockable->fReaderCount++;
256 if (readerCount > 0)
257 _kern_unblock_threads(readers, readerCount, B_OK);