Added pthread rwlock support.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25470 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+45
-2
@@ -6,6 +6,7 @@
|
||||
#define _PTHREAD_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
@@ -17,8 +18,8 @@ typedef struct _pthread_cond *pthread_cond_t;
|
||||
typedef struct _pthread_condattr *pthread_condattr_t;
|
||||
typedef int pthread_key_t;
|
||||
typedef struct _pthread_once pthread_once_t;
|
||||
typedef struct _pthread_rwlock *pthread_rwlock_t;
|
||||
typedef struct _pthread_rwlockattr *pthread_rwlockattr_t;
|
||||
typedef struct _pthread_rwlock pthread_rwlock_t;
|
||||
typedef struct _pthread_rwlockattr pthread_rwlockattr_t;
|
||||
typedef struct _pthread_barrier *pthread_barrier_t;
|
||||
typedef struct _pthread_barrierattr *pthread_barrierattr_t;
|
||||
typedef struct _pthread_spinlock *pthread_spinlock_t;
|
||||
@@ -28,6 +29,26 @@ struct _pthread_once {
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
struct _pthread_rwlock {
|
||||
uint32_t flags;
|
||||
int32_t owner;
|
||||
union {
|
||||
struct {
|
||||
int32_t sem;
|
||||
} shared;
|
||||
struct {
|
||||
int32_t lock_sem;
|
||||
int32_t lock_count;
|
||||
int32_t reader_count;
|
||||
int32_t writer_count;
|
||||
void* waiters[2];
|
||||
} local;
|
||||
};
|
||||
};
|
||||
|
||||
struct _pthread_rwlockattr {
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
enum pthread_mutex_type {
|
||||
PTHREAD_MUTEX_DEFAULT,
|
||||
@@ -159,6 +180,28 @@ extern int pthread_condattr_getpshared(const pthread_condattr_t *condAttr,
|
||||
extern int pthread_condattr_setpshared(pthread_condattr_t *condAttr,
|
||||
int processShared);
|
||||
|
||||
/* rwlock functions */
|
||||
extern int pthread_rwlock_init(pthread_rwlock_t *lock,
|
||||
const pthread_rwlockattr_t *attr);
|
||||
extern int pthread_rwlock_destroy(pthread_rwlock_t *lock);
|
||||
extern int pthread_rwlock_rdlock(pthread_rwlock_t *lock);
|
||||
extern int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock);
|
||||
extern int pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,
|
||||
const struct timespec *timeout);
|
||||
extern int pthread_rwlock_wrlock(pthread_rwlock_t *lock);
|
||||
extern int pthread_rwlock_trywrlock(pthread_rwlock_t *lock);
|
||||
extern int pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,
|
||||
const struct timespec *timeout);
|
||||
extern int pthread_rwlock_unlock(pthread_rwlock_t *lock);
|
||||
|
||||
/* rwlock attribute functions */
|
||||
extern int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
|
||||
extern int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
|
||||
extern int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr,
|
||||
int *shared);
|
||||
extern int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr,
|
||||
int shared);
|
||||
|
||||
/* misc. functions */
|
||||
extern int pthread_atfork(void (*prepare)(void), void (*parent)(void),
|
||||
void (*child)(void));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SubDir HAIKU_TOP src system libroot posix pthread ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
UsePrivateHeaders libroot ;
|
||||
UsePrivateHeaders libroot shared ;
|
||||
|
||||
MergeObject posix_pthread.o :
|
||||
pthread.c
|
||||
@@ -15,5 +15,6 @@ MergeObject posix_pthread.o :
|
||||
pthread_mutex.c
|
||||
pthread_mutexattr.c
|
||||
pthread_once.c
|
||||
pthread_rwlock.cpp
|
||||
;
|
||||
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <Debug.h>
|
||||
|
||||
#include <AutoLocker.h>
|
||||
#include <libroot_lock.h>
|
||||
#include <syscalls.h>
|
||||
#include <user_thread.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
|
||||
#define MAX_READER_COUNT 1000000
|
||||
|
||||
#define RWLOCK_FLAG_SHARED 0x01
|
||||
|
||||
|
||||
struct Waiter : DoublyLinkedListLinkImpl<Waiter> {
|
||||
Waiter(bool writer)
|
||||
:
|
||||
userThread(get_user_thread()),
|
||||
thread(find_thread(NULL)),
|
||||
writer(writer),
|
||||
queued(false)
|
||||
{
|
||||
}
|
||||
|
||||
user_thread* userThread;
|
||||
thread_id thread;
|
||||
status_t status;
|
||||
bool writer;
|
||||
bool queued;
|
||||
};
|
||||
|
||||
typedef DoublyLinkedList<Waiter> WaiterList;
|
||||
|
||||
|
||||
struct SharedRWLock {
|
||||
uint32_t flags;
|
||||
int32_t owner;
|
||||
int32_t sem;
|
||||
|
||||
status_t Init()
|
||||
{
|
||||
flags = RWLOCK_FLAG_SHARED;
|
||||
owner = -1;
|
||||
sem = create_sem(MAX_READER_COUNT, "pthread rwlock");
|
||||
|
||||
return sem >= 0 ? B_OK : EAGAIN;
|
||||
}
|
||||
|
||||
status_t Destroy()
|
||||
{
|
||||
if (sem < 0)
|
||||
return B_BAD_VALUE;
|
||||
return delete_sem(sem) == B_OK ? B_OK : B_BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t ReadLock(bigtime_t timeout)
|
||||
{
|
||||
return acquire_sem_etc(sem, 1,
|
||||
timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
|
||||
}
|
||||
|
||||
status_t WriteLock(bigtime_t timeout)
|
||||
{
|
||||
status_t error = acquire_sem_etc(sem, MAX_READER_COUNT,
|
||||
timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
|
||||
if (error == B_OK)
|
||||
owner = find_thread(NULL);
|
||||
return error;
|
||||
}
|
||||
|
||||
status_t Unlock()
|
||||
{
|
||||
if (find_thread(NULL) == owner) {
|
||||
owner = -1;
|
||||
return release_sem_etc(sem, MAX_READER_COUNT, 0);
|
||||
} else
|
||||
return release_sem(sem);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct LocalRWLock {
|
||||
uint32_t flags;
|
||||
int32_t owner;
|
||||
int32_t lock_sem;
|
||||
int32_t lock_count;
|
||||
int32_t reader_count;
|
||||
int32_t writer_count;
|
||||
WaiterList waiters;
|
||||
|
||||
status_t Init()
|
||||
{
|
||||
flags = 0;
|
||||
owner = -1;
|
||||
lock_sem = create_sem(0, "pthread rwlock");
|
||||
lock_count = 1;
|
||||
reader_count = 0;
|
||||
writer_count = 0;
|
||||
new(&waiters) WaiterList;
|
||||
|
||||
return lock_sem >= 0 ? B_OK : EAGAIN;
|
||||
}
|
||||
|
||||
status_t Destroy()
|
||||
{
|
||||
if (lock_sem < 0)
|
||||
return B_BAD_VALUE;
|
||||
return delete_sem(lock_sem) == B_OK ? B_OK : B_BAD_VALUE;
|
||||
}
|
||||
|
||||
bool StructureLock()
|
||||
{
|
||||
if (atomic_add((int32*)&lock_count, -1) <= 0)
|
||||
acquire_sem(lock_sem);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StructureUnlock()
|
||||
{
|
||||
if (atomic_add((int32*)&lock_count, 1) < 0)
|
||||
release_sem(lock_sem);
|
||||
}
|
||||
|
||||
status_t ReadLock(bigtime_t timeout)
|
||||
{
|
||||
Locker locker(this);
|
||||
|
||||
if (writer_count == 0) {
|
||||
reader_count++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return _Wait(false, timeout);
|
||||
}
|
||||
|
||||
status_t WriteLock(bigtime_t timeout)
|
||||
{
|
||||
Locker locker(this);
|
||||
|
||||
if (reader_count == 0 && writer_count == 0) {
|
||||
writer_count++;
|
||||
owner = find_thread(NULL);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return _Wait(true, timeout);
|
||||
}
|
||||
|
||||
status_t Unlock()
|
||||
{
|
||||
Locker locker(this);
|
||||
|
||||
if (find_thread(NULL) == owner) {
|
||||
writer_count--;
|
||||
owner = -1;
|
||||
} else
|
||||
reader_count--;
|
||||
|
||||
_Unblock();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
status_t _Wait(bool writer, bigtime_t timeout)
|
||||
{
|
||||
if (timeout == 0)
|
||||
return B_TIMED_OUT;
|
||||
|
||||
Waiter waiter(writer);
|
||||
waiters.Add(&waiter);
|
||||
waiter.queued = true;
|
||||
waiter.userThread->wait_status = 1;
|
||||
|
||||
if (writer)
|
||||
writer_count++;
|
||||
|
||||
StructureUnlock();
|
||||
status_t error = _kern_block_thread(
|
||||
timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout);
|
||||
StructureLock();
|
||||
|
||||
if (!waiter.queued)
|
||||
return waiter.status;
|
||||
|
||||
// we're still queued, which means an error (timeout, interrupt)
|
||||
// occurred
|
||||
waiters.Remove(&waiter);
|
||||
|
||||
if (writer)
|
||||
writer_count--;
|
||||
|
||||
_Unblock();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void _Unblock()
|
||||
{
|
||||
// Check whether there any waiting threads at all and whether anyone
|
||||
// has the write lock
|
||||
Waiter* waiter = waiters.Head();
|
||||
if (waiter == NULL || owner >= 0)
|
||||
return;
|
||||
|
||||
// writer at head of queue?
|
||||
if (waiter->writer) {
|
||||
if (reader_count == 0) {
|
||||
waiter->status = B_OK;
|
||||
waiter->queued = false;
|
||||
waiters.Remove(waiter);
|
||||
owner = waiter->thread;
|
||||
|
||||
if (waiter->userThread->wait_status > 0) {
|
||||
waiter->userThread->wait_status = B_OK;
|
||||
_kern_unblock_thread(waiter->thread, B_OK);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// wake up one or more readers -- we unblock more than one reader at
|
||||
// a time to save trips to the kernel
|
||||
while (!waiters.IsEmpty() && !waiters.Head()->writer) {
|
||||
static const int kMaxReaderUnblockCount = 128;
|
||||
thread_id readers[kMaxReaderUnblockCount];
|
||||
int readerCount = 0;
|
||||
|
||||
while (readerCount < kMaxReaderUnblockCount
|
||||
&& (waiter = waiters.Head()) != NULL
|
||||
&& !waiter->writer) {
|
||||
waiter->status = B_OK;
|
||||
waiter->queued = false;
|
||||
waiters.Remove(waiter);
|
||||
|
||||
if (waiter->userThread->wait_status > 0) {
|
||||
waiter->userThread->wait_status = B_OK;
|
||||
readers[readerCount++] = waiter->thread;
|
||||
}
|
||||
}
|
||||
|
||||
if (readerCount > 0)
|
||||
_kern_unblock_threads(readers, readerCount, B_OK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Locking {
|
||||
inline bool Lock(LocalRWLock* lockable)
|
||||
{
|
||||
return lockable->StructureLock();
|
||||
}
|
||||
|
||||
inline void Unlock(LocalRWLock* lockable)
|
||||
{
|
||||
lockable->StructureUnlock();
|
||||
}
|
||||
};
|
||||
typedef AutoLocker<LocalRWLock, Locking> Locker;
|
||||
};
|
||||
|
||||
|
||||
static void inline
|
||||
assert_dummy()
|
||||
{
|
||||
STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(SharedRWLock));
|
||||
STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(LocalRWLock));
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - public lock functions
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_init(pthread_rwlock_t* lock, const pthread_rwlockattr_t* attr)
|
||||
{
|
||||
bool shared = attr != NULL && (attr->flags & RWLOCK_FLAG_SHARED) != 0;
|
||||
|
||||
if (shared)
|
||||
return ((SharedRWLock*)lock)->Init();
|
||||
else
|
||||
return ((LocalRWLock*)lock)->Init();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_destroy(pthread_rwlock_t* lock)
|
||||
{
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
return ((SharedRWLock*)lock)->Destroy();
|
||||
else
|
||||
return ((LocalRWLock*)lock)->Destroy();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_rdlock(pthread_rwlock_t* lock)
|
||||
{
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
return ((SharedRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT);
|
||||
else
|
||||
return ((LocalRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_tryrdlock(pthread_rwlock_t* lock)
|
||||
{
|
||||
status_t error;
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
error = ((SharedRWLock*)lock)->ReadLock(0);
|
||||
else
|
||||
error = ((LocalRWLock*)lock)->ReadLock(0);
|
||||
|
||||
return error == B_TIMED_OUT ? EBUSY : error;
|
||||
}
|
||||
|
||||
|
||||
int pthread_rwlock_timedrdlock(pthread_rwlock_t* lock,
|
||||
const struct timespec *timeout)
|
||||
{
|
||||
bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL
|
||||
+ timeout->tv_nsec / 1000LL;
|
||||
|
||||
status_t error;
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
error = ((SharedRWLock*)lock)->ReadLock(timeoutMicros);
|
||||
else
|
||||
error = ((LocalRWLock*)lock)->ReadLock(timeoutMicros);
|
||||
|
||||
return error == B_TIMED_OUT ? EBUSY : error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_wrlock(pthread_rwlock_t* lock)
|
||||
{
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
return ((SharedRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT);
|
||||
else
|
||||
return ((LocalRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_trywrlock(pthread_rwlock_t* lock)
|
||||
{
|
||||
status_t error;
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
error = ((SharedRWLock*)lock)->WriteLock(0);
|
||||
else
|
||||
error = ((LocalRWLock*)lock)->WriteLock(0);
|
||||
|
||||
return error == B_TIMED_OUT ? EBUSY : error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_timedwrlock(pthread_rwlock_t* lock,
|
||||
const struct timespec *timeout)
|
||||
{
|
||||
bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL
|
||||
+ timeout->tv_nsec / 1000LL;
|
||||
|
||||
status_t error;
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
error = ((SharedRWLock*)lock)->WriteLock(timeoutMicros);
|
||||
else
|
||||
error = ((LocalRWLock*)lock)->WriteLock(timeoutMicros);
|
||||
|
||||
return error == B_TIMED_OUT ? EBUSY : error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlock_unlock(pthread_rwlock_t* lock)
|
||||
{
|
||||
if ((lock->flags & RWLOCK_FLAG_SHARED) != 0)
|
||||
return ((SharedRWLock*)lock)->Unlock();
|
||||
else
|
||||
return ((LocalRWLock*)lock)->Unlock();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - public attribute functions
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlockattr_init(pthread_rwlockattr_t* attr)
|
||||
{
|
||||
attr->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* shared)
|
||||
{
|
||||
*shared = (attr->flags & RWLOCK_FLAG_SHARED) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int shared)
|
||||
{
|
||||
if (shared)
|
||||
attr->flags |= RWLOCK_FLAG_SHARED;
|
||||
else
|
||||
attr->flags &= ~RWLOCK_FLAG_SHARED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user