2018-01-25 13:40:13 +01:00
|
|
|
/*
|
|
|
|
* \brief POSIX readers/writer lock (rwlock) implementation
|
|
|
|
* \author Alexander Senier
|
|
|
|
* \date 2018-01-25
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2018 Componolit GmbH
|
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
|
|
|
#include <base/log.h>
|
|
|
|
#include <base/lock.h>
|
|
|
|
#include <base/lock_guard.h>
|
|
|
|
#include <base/thread.h>
|
2019-05-23 12:30:18 +02:00
|
|
|
#include <libc/allocator.h>
|
2018-01-25 13:40:13 +01:00
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
/* libc includes */
|
2018-01-25 13:40:13 +01:00
|
|
|
#include <errno.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
/* libc-internal includes */
|
|
|
|
#include <internal/types.h>
|
|
|
|
|
|
|
|
using namespace Libc;
|
|
|
|
|
2019-05-23 12:30:18 +02:00
|
|
|
|
2018-01-25 13:40:13 +01:00
|
|
|
/*
|
|
|
|
* A reader-preferring implementation of a readers-writer lock as described
|
|
|
|
* in Michael Raynal, "Concurrent Programming: Algorithms, Principles, and
|
|
|
|
* Foundations", ISBN 978-3-642-32026-2, page 75
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This class is named 'struct pthread_rwlock' because the 'pthread_rwlock_t'
|
|
|
|
* type is defined as 'struct rwlock*' in 'sys/_pthreadtypes.h'
|
|
|
|
*/
|
|
|
|
struct pthread_rwlock
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
Thread *_owner = nullptr;
|
|
|
|
Lock _nbr_mutex {};
|
|
|
|
Lock _global_mutex {};
|
2018-01-25 13:40:13 +01:00
|
|
|
int _nbr = 0;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
void rdlock()
|
|
|
|
{
|
2019-09-19 20:37:17 +02:00
|
|
|
Lock_guard<Lock> guard(_nbr_mutex);
|
2018-01-25 13:40:13 +01:00
|
|
|
++_nbr;
|
|
|
|
if (_nbr == 1) {
|
|
|
|
_global_mutex.lock();
|
2018-04-09 07:13:17 +02:00
|
|
|
_owner = nullptr;
|
2018-01-25 13:40:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void wrlock()
|
|
|
|
{
|
|
|
|
_global_mutex.lock();
|
2019-09-19 20:37:17 +02:00
|
|
|
_owner = Thread::myself();
|
2018-01-25 13:40:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int unlock()
|
|
|
|
{
|
|
|
|
/* Read lock */
|
|
|
|
if (_owner == nullptr) {
|
2019-09-19 20:37:17 +02:00
|
|
|
Lock_guard<Lock> guard(_nbr_mutex);
|
2018-01-25 13:40:13 +01:00
|
|
|
_nbr--;
|
|
|
|
if (_nbr == 0) {
|
2018-04-09 07:13:17 +02:00
|
|
|
_owner = nullptr;
|
2018-01-25 13:40:13 +01:00
|
|
|
_global_mutex.unlock();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2019-09-19 20:37:17 +02:00
|
|
|
if (_owner != Thread::myself()) {
|
|
|
|
error("Unlocking writer lock owned by other thread");
|
2018-01-25 13:40:13 +01:00
|
|
|
errno = EPERM;
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Write lock owned by us */
|
2018-04-09 07:13:17 +02:00
|
|
|
_owner = nullptr;
|
2018-01-25 13:40:13 +01:00
|
|
|
_global_mutex.unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pthread_rwlockattr
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
2018-11-13 13:33:45 +01:00
|
|
|
static int rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
|
|
|
|
{
|
2019-09-19 20:37:17 +02:00
|
|
|
static Lock rwlock_init_lock { };
|
2018-11-13 13:33:45 +01:00
|
|
|
|
|
|
|
if (!rwlock)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
try {
|
2019-09-19 20:37:17 +02:00
|
|
|
Lock::Guard g(rwlock_init_lock);
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
*rwlock = new (alloc) struct pthread_rwlock();
|
2018-11-13 13:33:45 +01:00
|
|
|
return 0;
|
|
|
|
} catch (...) { return ENOMEM; }
|
|
|
|
}
|
|
|
|
|
2018-01-25 13:40:13 +01:00
|
|
|
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
|
|
|
|
{
|
2018-11-13 13:33:45 +01:00
|
|
|
return rwlock_init(rwlock, attr);
|
2018-01-25 13:40:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
|
|
|
|
{
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, *rwlock);
|
2018-01-25 13:40:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)
|
|
|
|
{
|
2018-11-13 13:33:45 +01:00
|
|
|
if (!rwlock)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
|
|
|
if (rwlock_init(rwlock, NULL))
|
|
|
|
return ENOMEM;
|
|
|
|
|
2018-01-25 13:40:13 +01:00
|
|
|
(*rwlock)->rdlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
|
|
|
|
{
|
2018-11-13 13:33:45 +01:00
|
|
|
if (!rwlock)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
|
|
|
if (rwlock_init(rwlock, NULL))
|
|
|
|
return ENOMEM;
|
|
|
|
|
2018-01-25 13:40:13 +01:00
|
|
|
(*rwlock)->wrlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
|
|
|
|
{
|
|
|
|
return (*rwlock)->unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
|
|
|
|
{
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
*attr = new (alloc) struct pthread_rwlockattr();
|
2018-01-25 13:40:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared)
|
|
|
|
{
|
|
|
|
*pshared = PTHREAD_PROCESS_PRIVATE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared)
|
|
|
|
{
|
|
|
|
if (pshared != PTHREAD_PROCESS_PRIVATE) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
|
|
|
|
{
|
2019-09-16 13:52:39 +02:00
|
|
|
Libc::Allocator alloc { };
|
|
|
|
destroy(alloc, *attr);
|
2018-01-25 13:40:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unimplemented functions:
|
|
|
|
* int pthread_rwlock_timedrdlock(pthread_rwlock_t *, const struct timespec *);
|
|
|
|
* int pthread_rwlock_timedwrlock(pthread_rwlock_t *, const struct timespec *);
|
|
|
|
* int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
|
|
|
|
* int pthread_rwlock_trywrlock(pthread_rwlock_t *);
|
|
|
|
*/
|
|
|
|
}
|