2 * Copyright 2006, Haiku.
3 * Distributed under the terms of the MIT License.
6 * IngoWeinhold <bonefish@cs.tu-berlin.de>
13 // info about a read lock owner
14 struct RWLocker::ReadLockInfo
{
29 fWriterWriterCount(0),
36 RWLocker::RWLocker(const char* name
)
44 fWriterWriterCount(0),
54 delete_sem(fMutex
.semaphore
);
55 delete_sem(fQueue
.semaphore
);
56 for (int32 i
= 0; ReadLockInfo
* info
= _ReadLockInfoAt(i
); i
++)
64 status_t error
= _ReadLock(B_INFINITE_TIMEOUT
);
65 return (error
== B_OK
);
68 // ReadLockWithTimeout
70 RWLocker::ReadLockWithTimeout(bigtime_t timeout
)
72 bigtime_t absoluteTimeout
= system_time() + timeout
;
73 // take care of overflow
74 if (timeout
> 0 && absoluteTimeout
< 0)
75 absoluteTimeout
= B_INFINITE_TIMEOUT
;
76 return _ReadLock(absoluteTimeout
);
81 RWLocker::ReadUnlock()
84 thread_id thread
= find_thread(NULL
);
85 if (thread
== fWriter
) {
86 // We (also) have a write lock.
87 if (fWriterReaderCount
> 0)
89 // else: error: unmatched ReadUnlock()
91 int32 index
= _IndexOf(thread
);
92 if (ReadLockInfo
* info
= _ReadLockInfoAt(index
)) {
94 if (--info
->count
== 0) {
95 // The outer read lock bracket for the thread has been
96 // reached. Dispose the info.
97 _DeleteReadLockInfo(index
);
99 if (fReaderCount
== 0) {
100 // The last reader needs to unlock the mutex.
101 _ReleaseBenaphore(fMutex
);
103 } // else: error: caller has no read lock
106 } // else: we are probably going to be destroyed
111 // Returns whether or not the calling thread owns a read lock or even a
114 RWLocker::IsReadLocked() const
118 thread_id thread
= find_thread(NULL
);
119 result
= (thread
== fWriter
|| _IndexOf(thread
) >= 0);
127 RWLocker::WriteLock()
129 status_t error
= _WriteLock(B_INFINITE_TIMEOUT
);
130 return (error
== B_OK
);
133 // WriteLockWithTimeout
135 RWLocker::WriteLockWithTimeout(bigtime_t timeout
)
137 bigtime_t absoluteTimeout
= system_time() + timeout
;
138 // take care of overflow
139 if (timeout
> 0 && absoluteTimeout
< 0)
140 absoluteTimeout
= B_INFINITE_TIMEOUT
;
141 return _WriteLock(absoluteTimeout
);
146 RWLocker::WriteUnlock()
149 thread_id thread
= find_thread(NULL
);
150 if (thread
== fWriter
) {
152 if (--fWriterWriterCount
== 0) {
153 // The outer write lock bracket for the thread has been
156 if (fWriterReaderCount
> 0) {
157 // We still own read locks.
158 _NewReadLockInfo(thread
, fWriterReaderCount
);
159 // A reader that expects to be the first reader may wait
160 // at the mutex semaphore. We need to wake it up.
161 if (fReaderCount
> 0)
162 _ReleaseBenaphore(fMutex
);
163 fReaderCount
+= fWriterReaderCount
;
164 fWriterReaderCount
= 0;
166 // We don't own any read locks. So we have to release the
168 _ReleaseBenaphore(fMutex
);
171 } // else: error: unmatched WriteUnlock()
173 } // else: We're probably going to die.
178 // Returns whether or not the calling thread owns a write lock.
180 RWLocker::IsWriteLocked() const
182 return (fWriter
== find_thread(NULL
));
187 RWLocker::_Init(const char* name
)
189 // init the mutex benaphore
190 BString
mutexName(name
);
191 mutexName
+= "_RWLocker_mutex";
192 fMutex
.semaphore
= create_sem(0, mutexName
.String());
194 // init the queueing benaphore
195 BString
queueName(name
);
196 queueName
+= "_RWLocker_queue";
197 fQueue
.semaphore
= create_sem(0, queueName
.String());
203 // /timeout/ -- absolute timeout
205 RWLocker::_ReadLock(bigtime_t timeout
)
207 status_t error
= B_OK
;
208 thread_id thread
= find_thread(NULL
);
211 // Check, if we already own a read (or write) lock. In this case we
212 // can skip the usual locking procedure.
213 if (thread
== fWriter
) {
214 // We already own a write lock.
215 fWriterReaderCount
++;
217 } else if (ReadLockInfo
* info
= _ReadLockInfoAt(_IndexOf(thread
))) {
218 // We already own a read lock.
224 } else // failed to lock the data
226 // Usual locking, i.e. we do not already own a read or write lock.
227 if (error
== B_OK
&& !locked
) {
228 error
= _AcquireBenaphore(fQueue
, timeout
);
231 bool firstReader
= false;
232 if (++fReaderCount
== 1) {
233 // We are the first reader.
234 _NewReadLockInfo(thread
);
237 _NewReadLockInfo(thread
);
239 // The first reader needs to lock the mutex.
241 error
= _AcquireBenaphore(fMutex
, timeout
);
249 _DeleteReadLockInfo(_IndexOf(thread
));
256 // Probably we are going to be destroyed.
260 // Let the next candidate enter the game.
261 _ReleaseBenaphore(fQueue
);
263 // We couldn't lock the data, which can only happen, if
264 // we're going to be destroyed.
274 // /timeout/ -- absolute timeout
276 RWLocker::_WriteLock(bigtime_t timeout
)
278 status_t error
= B_ERROR
;
280 bool infiniteTimeout
= (timeout
== B_INFINITE_TIMEOUT
);
282 int32 readerCount
= 0;
283 thread_id thread
= find_thread(NULL
);
284 int32 index
= _IndexOf(thread
);
285 if (ReadLockInfo
* info
= _ReadLockInfoAt(index
)) {
286 // We already own a read lock.
287 if (fWriterCount
> 0) {
288 // There are writers before us.
289 if (infiniteTimeout
) {
290 // Timeout is infinite and there are writers before us.
291 // Unregister the read locks and lock as usual.
292 readerCount
= info
->count
;
294 fReaderCount
-= readerCount
;
295 _DeleteReadLockInfo(index
);
298 // The timeout is finite and there are readers before us:
299 // let the write lock request fail.
300 error
= B_WOULD_BLOCK
;
302 } else if (info
->count
== fReaderCount
) {
303 // No writers before us.
304 // We are the only read lock owners. Just move the read lock
305 // info data to the special writer fields and then we are done.
306 // Note: At this point we may overtake readers that already
307 // have acquired the queueing benaphore, but have not yet
308 // locked the data. But that doesn't harm.
311 fWriterWriterCount
= 1;
312 fWriterReaderCount
= info
->count
;
313 fReaderCount
-= fWriterReaderCount
;
314 _DeleteReadLockInfo(index
);
318 // No writers before us, but other readers.
319 // Note, we're quite restrictive here. If there are only
320 // readers before us, we could reinstall our readers, if
321 // our request times out. Unfortunately it is not easy
322 // to ensure, that no writer overtakes us between unlocking
323 // the data and acquiring the queuing benaphore.
324 if (infiniteTimeout
) {
325 // Unregister the readers and lock as usual.
326 readerCount
= info
->count
;
328 fReaderCount
-= readerCount
;
329 _DeleteReadLockInfo(index
);
332 error
= B_WOULD_BLOCK
;
335 // We don't own a read lock.
336 if (fWriter
== thread
) {
337 // ... but a write lock.
339 fWriterWriterCount
++;
343 // We own neither read nor write locks.
351 // First step: acquire the queueing benaphore.
352 if (!locked
&& error
== B_OK
) {
353 error
= _AcquireBenaphore(fQueue
, timeout
);
362 } // else: failed to lock the data: we're probably going
367 // Probably we're going to die.
371 // Second step: acquire the mutex benaphore.
372 if (!locked
&& error
== B_OK
) {
373 error
= _AcquireBenaphore(fMutex
, timeout
);
376 // Yeah, we made it. Set the special writer fields.
378 fWriterWriterCount
= 1;
379 fWriterReaderCount
= readerCount
;
387 } // else: failed to lock the data: we're probably going
392 // Probably we're going to die.
395 // Whatever happened, we have to release the queueing benaphore.
396 _ReleaseBenaphore(fQueue
);
398 } else // failed to lock the data
405 RWLocker::_AddReadLockInfo(ReadLockInfo
* info
)
407 int32 index
= fReadLockInfos
.CountItems();
408 fReadLockInfos
.AddItem(info
, index
);
414 // Create a new read lock info for the supplied thread and add it to the
415 // list. Returns the index of the info.
417 RWLocker::_NewReadLockInfo(thread_id thread
, int32 count
)
419 ReadLockInfo
* info
= new ReadLockInfo
;
420 info
->reader
= thread
;
422 return _AddReadLockInfo(info
);
425 // _DeleteReadLockInfo
427 RWLocker::_DeleteReadLockInfo(int32 index
)
429 if (ReadLockInfo
* info
= (ReadLockInfo
*)fReadLockInfos
.RemoveItem(index
))
434 RWLocker::ReadLockInfo
*
435 RWLocker::_ReadLockInfoAt(int32 index
) const
437 return (ReadLockInfo
*)fReadLockInfos
.ItemAt(index
);
442 RWLocker::_IndexOf(thread_id thread
) const
444 int32 count
= fReadLockInfos
.CountItems();
445 for (int32 i
= 0; i
< count
; i
++) {
446 if (_ReadLockInfoAt(i
)->reader
== thread
)
454 RWLocker::_AcquireBenaphore(Benaphore
& benaphore
, bigtime_t timeout
)
456 status_t error
= B_OK
;
457 if (atomic_add(&benaphore
.counter
, 1) > 0) {
458 error
= acquire_sem_etc(benaphore
.semaphore
, 1, B_ABSOLUTE_TIMEOUT
,
466 RWLocker::_ReleaseBenaphore(Benaphore
& benaphore
)
468 if (atomic_add(&benaphore
.counter
, -1) > 1)
469 release_sem(benaphore
.semaphore
);