R/W lock implementation:

* Changed the rw_lock_{read,write}_unlock() return values to void. They
  returned a value != B_OK only in case of user error and no-one checked them
  anyway.
* Optimized rw_lock_read_[un]lock(). They are inline now and as long as
  there's no contending write locker, they will only perform an atomic_add().
* Changed the semantics of nested locking after acquiring a write lock: Read
  and write locks are counted separately, so read locks no longer implicitly
  become write locks. This does e.g. make degrading a write lock to a read
  lock by way of read_lock + write_unlock (as used in the VM) actually work.

These changes speed up the -j8 Haiku image build on my machine by a few
percent, but more interestingly they reduce the total kernel time by 25 %.
Apparently we get more contention on other locks, now.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34830 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-12-31 17:03:41 +00:00
parent 4ccd636dcb
commit 2ea2527fe4
4 changed files with 290 additions and 156 deletions
+45 -6
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected].
* Copyright 2008-2009, Ingo Weinhold, [email protected].
* Copyright 2002-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
@@ -45,12 +45,21 @@ typedef struct rw_lock {
const char* name;
struct rw_lock_waiter* waiters;
thread_id holder;
int32 reader_count;
int32 writer_count;
vint32 count;
int32 owner_count;
int16 active_readers;
// Only > 0 while a writer is waiting: number
// of active readers when the first waiting
// writer started waiting.
int16 pending_readers;
// Number of readers that have already
// incremented "count", but have not yet started
// to wait at the time the last writer unlocked.
uint32 flags;
} rw_lock;
#define RW_LOCK_WRITER_COUNT_BASE 0x10000
#define RW_LOCK_FLAG_CLONE_NAME 0x1
@@ -114,10 +123,8 @@ extern void rw_lock_init(rw_lock* lock, const char* name);
// name is *not* cloned nor freed in rw_lock_destroy()
extern void rw_lock_init_etc(rw_lock* lock, const char* name, uint32 flags);
extern void rw_lock_destroy(rw_lock* lock);
extern status_t rw_lock_read_lock(rw_lock* lock);
extern status_t rw_lock_read_unlock(rw_lock* lock);
extern status_t rw_lock_write_lock(rw_lock* lock);
extern status_t rw_lock_write_unlock(rw_lock* lock);
extern void rw_lock_write_unlock(rw_lock* lock);
extern void mutex_init(mutex* lock, const char* name);
// name is *not* cloned nor freed in mutex_destroy()
@@ -129,7 +136,12 @@ extern status_t mutex_switch_lock(mutex* from, mutex* to);
// to, the operation is safe as long as "from" is held while destroying
// "to".
// implementation private:
extern status_t _rw_lock_read_lock(rw_lock* lock);
extern void _rw_lock_read_unlock(rw_lock* lock);
extern status_t _mutex_lock(mutex* lock, bool threadsLocked);
extern void _mutex_unlock(mutex* lock, bool threadsLocked);
extern status_t _mutex_trylock(mutex* lock);
@@ -137,6 +149,33 @@ extern status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags,
bigtime_t timeout);
static inline status_t
rw_lock_read_lock(rw_lock* lock)
{
#if KDEBUG_RW_LOCK_DEBUG
return rw_lock_write_lock(lock);
#else
int32 oldCount = atomic_add(&lock->count, 1);
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
return _rw_lock_read_lock(lock);
return B_OK;
#endif
}
static inline void
rw_lock_read_unlock(rw_lock* lock)
{
#if KDEBUG_RW_LOCK_DEBUG
rw_lock_write_unlock(lock);
#else
int32 oldCount = atomic_add(&lock->count, -1);
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
_rw_lock_read_unlock(lock);
#endif
}
static inline status_t
mutex_lock(mutex* lock)
{