648bcd1505
This patch unifies the patterns of using the 'Genode' and 'Libc' namespaces. Types defined in the 'internal/' headers reside in the 'Libc' namespace. The code in the headers does not need to use the 'Libc::' prefix. Compilation units import the 'Libc' namespace after the definition of local types. Local types reside in the 'Libc' namespace (and should eventually move to an 'internal/' header). Since the 'Libc' namespace imports the 'Genode' namespace, there is no need to use the 'Genode::' prefix. Consequently, code in the compilation units rarely need to qualify the 'Genode' or 'Libc' namespaces. There are a few cases where the 'Libc', the 'Genode', and the global (libc) namespaces are ambigious. In these cases, an explicit clarification is needed: - 'Genode::Allocator' differs from 'Libc::Allocator'. - 'Genode::Env' differs from 'Libc::Env'. - Genode's string functions (strcmp, memcpy, strcpy) conflict with the names of the (global) libc functions. - There exist both 'Genode::uint64_t' and the libc'c 'uint64_t'. Issue #3497
195 lines
3.8 KiB
C++
195 lines
3.8 KiB
C++
/*
|
|
* \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>
|
|
#include <libc/allocator.h>
|
|
|
|
/* libc includes */
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
|
|
/* libc-internal includes */
|
|
#include <internal/types.h>
|
|
|
|
using namespace Libc;
|
|
|
|
|
|
/*
|
|
* 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:
|
|
|
|
Thread *_owner = nullptr;
|
|
Lock _nbr_mutex {};
|
|
Lock _global_mutex {};
|
|
int _nbr = 0;
|
|
|
|
public:
|
|
|
|
void rdlock()
|
|
{
|
|
Lock_guard<Lock> guard(_nbr_mutex);
|
|
++_nbr;
|
|
if (_nbr == 1) {
|
|
_global_mutex.lock();
|
|
_owner = nullptr;
|
|
}
|
|
}
|
|
|
|
void wrlock()
|
|
{
|
|
_global_mutex.lock();
|
|
_owner = Thread::myself();
|
|
}
|
|
|
|
int unlock()
|
|
{
|
|
/* Read lock */
|
|
if (_owner == nullptr) {
|
|
Lock_guard<Lock> guard(_nbr_mutex);
|
|
_nbr--;
|
|
if (_nbr == 0) {
|
|
_owner = nullptr;
|
|
_global_mutex.unlock();
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
if (_owner != Thread::myself()) {
|
|
error("Unlocking writer lock owned by other thread");
|
|
errno = EPERM;
|
|
return -1;
|
|
};
|
|
|
|
/* Write lock owned by us */
|
|
_owner = nullptr;
|
|
_global_mutex.unlock();
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
struct pthread_rwlockattr
|
|
{
|
|
};
|
|
|
|
static int rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
|
|
{
|
|
static Lock rwlock_init_lock { };
|
|
|
|
if (!rwlock)
|
|
return EINVAL;
|
|
|
|
try {
|
|
Lock::Guard g(rwlock_init_lock);
|
|
Libc::Allocator alloc { };
|
|
*rwlock = new (alloc) struct pthread_rwlock();
|
|
return 0;
|
|
} catch (...) { return ENOMEM; }
|
|
}
|
|
|
|
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
|
|
{
|
|
return rwlock_init(rwlock, attr);
|
|
}
|
|
|
|
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
|
|
{
|
|
Libc::Allocator alloc { };
|
|
destroy(alloc, *rwlock);
|
|
return 0;
|
|
}
|
|
|
|
int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)
|
|
{
|
|
if (!rwlock)
|
|
return EINVAL;
|
|
|
|
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
|
if (rwlock_init(rwlock, NULL))
|
|
return ENOMEM;
|
|
|
|
(*rwlock)->rdlock();
|
|
return 0;
|
|
}
|
|
|
|
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
|
|
{
|
|
if (!rwlock)
|
|
return EINVAL;
|
|
|
|
if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)
|
|
if (rwlock_init(rwlock, NULL))
|
|
return ENOMEM;
|
|
|
|
(*rwlock)->wrlock();
|
|
return 0;
|
|
}
|
|
|
|
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
|
|
{
|
|
return (*rwlock)->unlock();
|
|
}
|
|
|
|
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
|
|
{
|
|
Libc::Allocator alloc { };
|
|
*attr = new (alloc) struct pthread_rwlockattr();
|
|
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)
|
|
{
|
|
Libc::Allocator alloc { };
|
|
destroy(alloc, *attr);
|
|
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 *);
|
|
*/
|
|
}
|