* _NewReadLockInfo() can no longer crash in case the allocation failed.

* _ReadLock() will now fail with B_NO_MEMORY in case the ReadLockInfo couldn't
  be created.
* Note, due to a design bug, we cannot guarantee that a previous read lock
  can be reestablished after releasing a write lock in case of low memory.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17332 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-05-05 10:10:05 +00:00
parent 854026c907
commit dde52de872

View File

@ -15,30 +15,26 @@ struct RWLocker::ReadLockInfo {
// constructor // constructor
RWLocker::RWLocker() RWLocker::RWLocker()
: fLock(), :
fMutex(), fReaderCount(0),
fQueue(), fWriterCount(0),
fReaderCount(0), fReadLockInfos(8),
fWriterCount(0), fWriter(B_ERROR),
fReadLockInfos(8), fWriterWriterCount(0),
fWriter(B_ERROR), fWriterReaderCount(0)
fWriterWriterCount(0),
fWriterReaderCount(0)
{ {
_Init(NULL); _Init(NULL);
} }
// constructor // constructor
RWLocker::RWLocker(const char* name) RWLocker::RWLocker(const char* name)
: fLock(), :
fMutex(), fReaderCount(0),
fQueue(), fWriterCount(0),
fReaderCount(0), fReadLockInfos(8),
fWriterCount(0), fWriter(B_ERROR),
fReadLockInfos(8), fWriterWriterCount(0),
fWriter(B_ERROR), fWriterReaderCount(0)
fWriterWriterCount(0),
fWriterReaderCount(0)
{ {
_Init(name); _Init(name);
} }
@ -49,8 +45,10 @@ RWLocker::~RWLocker()
_AcquireBenaphore(fLock); _AcquireBenaphore(fLock);
delete_sem(fMutex.semaphore); delete_sem(fMutex.semaphore);
delete_sem(fQueue.semaphore); delete_sem(fQueue.semaphore);
for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++) for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++) {
delete info; delete info;
}
delete_sem(fLock.semaphore); delete_sem(fLock.semaphore);
} }
@ -64,6 +62,7 @@ RWLocker::InitCheck() const
return fMutex.semaphore; return fMutex.semaphore;
if (fQueue.semaphore < 0) if (fQueue.semaphore < 0)
return fQueue.semaphore; return fQueue.semaphore;
return B_OK; return B_OK;
} }
@ -116,10 +115,11 @@ RWLocker::ReadUnlock()
} // else: we are probably going to be destroyed } // else: we are probably going to be destroyed
} }
// IsReadLocked
// /** Returns whether or not the calling thread owns a read lock or, if
// Returns whether or not the calling thread owns a read lock or, if * orWriteLock is true, at least a write lock.
// orWriteLock is true, at least a write lock. */
bool bool
RWLocker::IsReadLocked(bool orWriteLock) const RWLocker::IsReadLocked(bool orWriteLock) const
{ {
@ -166,10 +166,13 @@ RWLocker::WriteUnlock()
if (fWriterReaderCount > 0) { if (fWriterReaderCount > 0) {
// We still own read locks. // We still own read locks.
_NewReadLockInfo(thread, fWriterReaderCount); _NewReadLockInfo(thread, fWriterReaderCount);
// TODO: if the creation fails, there is nothing we can do about it!
// A reader that expects to be the first reader may wait // A reader that expects to be the first reader may wait
// at the mutex semaphore. We need to wake it up. // at the mutex semaphore. We need to wake it up.
if (fReaderCount > 0) if (fReaderCount > 0)
_ReleaseBenaphore(fMutex); _ReleaseBenaphore(fMutex);
fReaderCount += fWriterReaderCount; fReaderCount += fWriterReaderCount;
fWriterReaderCount = 0; fWriterReaderCount = 0;
} else { } else {
@ -183,9 +186,9 @@ RWLocker::WriteUnlock()
} // else: We're probably going to die. } // else: We're probably going to die.
} }
// IsWriteLocked
// /** Returns whether or not the calling thread owns a write lock. */
// Returns whether or not the calling thread owns a write lock.
bool bool
RWLocker::IsWriteLocked() const RWLocker::IsWriteLocked() const
{ {
@ -193,8 +196,7 @@ RWLocker::IsWriteLocked() const
} }
// make_sem_name // make_sem_name
static static void
void
make_sem_name(char *buffer, const char *name, const char *suffix) make_sem_name(char *buffer, const char *name, const char *suffix)
{ {
if (!name) if (!name)
@ -252,8 +254,11 @@ RWLocker::_ReadLock(bigtime_t timeout)
locked = true; locked = true;
} }
_ReleaseBenaphore(fLock); _ReleaseBenaphore(fLock);
} else // failed to lock the data } else {
// failed to lock the data
error = B_ERROR; error = B_ERROR;
}
// Usual locking, i.e. we do not already own a read or write lock. // Usual locking, i.e. we do not already own a read or write lock.
if (error == B_OK && !locked) { if (error == B_OK && !locked) {
error = _AcquireBenaphore(fQueue, timeout); error = _AcquireBenaphore(fQueue, timeout);
@ -262,11 +267,18 @@ RWLocker::_ReadLock(bigtime_t timeout)
bool firstReader = false; bool firstReader = false;
if (++fReaderCount == 1) { if (++fReaderCount == 1) {
// We are the first reader. // We are the first reader.
_NewReadLockInfo(thread);
firstReader = true; firstReader = true;
} else }
_NewReadLockInfo(thread);
int32 index = _NewReadLockInfo(thread);
_ReleaseBenaphore(fLock); _ReleaseBenaphore(fLock);
if (index < 0) {
// creating a ReadLockInfo object failed
_ReleaseBenaphore(fQueue);
return B_NO_MEMORY;
}
// The first reader needs to lock the mutex. // The first reader needs to lock the mutex.
if (firstReader) { if (firstReader) {
error = _AcquireBenaphore(fMutex, timeout); error = _AcquireBenaphore(fMutex, timeout);
@ -399,6 +411,7 @@ RWLocker::_WriteLock(bigtime_t timeout)
break; break;
} }
} }
// Second step: acquire the mutex benaphore. // Second step: acquire the mutex benaphore.
if (!locked && error == B_OK) { if (!locked && error == B_OK) {
error = _AcquireBenaphore(fMutex, timeout); error = _AcquireBenaphore(fMutex, timeout);
@ -440,14 +453,19 @@ RWLocker::_AddReadLockInfo(ReadLockInfo* info)
return index; return index;
} }
// _NewReadLockInfo
// /** Create a new read lock info for the supplied thread and add it to
// Create a new read lock info for the supplied thread and add it to the * the list. Returns the index of the info, or -1 to indicate an out
// list. Returns the index of the info. * of memory situation.
*/
int32 int32
RWLocker::_NewReadLockInfo(thread_id thread, int32 count) RWLocker::_NewReadLockInfo(thread_id thread, int32 count)
{ {
ReadLockInfo* info = new(nothrow) ReadLockInfo; ReadLockInfo* info = new(nothrow) ReadLockInfo;
if (info == NULL)
return -1;
info->reader = thread; info->reader = thread;
info->count = count; info->count = count;
return _AddReadLockInfo(info); return _AddReadLockInfo(info);