2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
6 #include <RWLockManager.h>
8 #include <AutoLocker.h>
11 #include <user_thread.h>
14 RWLockable::RWLockable()
23 RWLockManager::RWLockManager()
25 fLock("r/w lock manager")
30 RWLockManager::~RWLockManager()
36 RWLockManager::ReadLock(RWLockable
* lockable
)
38 AutoLocker
<RWLockManager
> locker(this);
40 if (lockable
->fWaiters
.IsEmpty()) {
41 lockable
->fReaderCount
++;
45 return _Wait(lockable
, false, B_INFINITE_TIMEOUT
) == B_OK
;
50 RWLockManager::TryReadLock(RWLockable
* lockable
)
52 AutoLocker
<RWLockManager
> locker(this);
54 if (lockable
->fWaiters
.IsEmpty()) {
55 lockable
->fReaderCount
++;
64 RWLockManager::ReadLockWithTimeout(RWLockable
* lockable
, bigtime_t timeout
)
66 AutoLocker
<RWLockManager
> locker(this);
68 if (lockable
->fWaiters
.IsEmpty()) {
69 lockable
->fReaderCount
++;
73 return _Wait(lockable
, false, timeout
);
78 RWLockManager::ReadUnlock(RWLockable
* lockable
)
80 AutoLocker
<RWLockManager
> locker(this);
82 if (lockable
->fReaderCount
<= 0) {
83 debugger("RWLockManager::ReadUnlock(): Not read-locked!");
87 if (--lockable
->fReaderCount
== 0)
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
++;
104 if (lockable
->fReaderCount
== 0 && lockable
->fWaiters
.IsEmpty()) {
105 lockable
->fOwnerCount
= 1;
106 lockable
->fOwner
= find_thread(NULL
);
110 return _Wait(lockable
, true, B_INFINITE_TIMEOUT
) == B_OK
;
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
++;
126 if (lockable
->fReaderCount
== 0 && lockable
->fWaiters
.IsEmpty()) {
127 lockable
->fOwnerCount
++;
128 lockable
->fOwner
= thread
;
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
++;
148 if (lockable
->fReaderCount
== 0 && lockable
->fWaiters
.IsEmpty()) {
149 lockable
->fOwnerCount
++;
150 lockable
->fOwner
= thread
;
154 return _Wait(lockable
, true, timeout
);
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 "
169 if (--lockable
->fOwnerCount
== 0) {
170 lockable
->fOwner
= -1;
177 RWLockManager::_Wait(RWLockable
* lockable
, bool writer
, bigtime_t timeout
)
183 RWLockable::Waiter
waiter(writer
);
184 lockable
->fWaiters
.Add(&waiter
);
185 waiter
.queued
= true;
186 get_user_thread()->wait_status
= 1;
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
);
202 return waiter
.status
;
204 // we're still queued, which means an error (timeout, interrupt)
206 lockable
->fWaiters
.Remove(&waiter
);
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)
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
);
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
];
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
++;
257 _kern_unblock_threads(readers
, readerCount
, B_OK
);