libevl/eshi/mutex.c

200 lines
3.9 KiB
C
Raw Normal View History

/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <unistd.h>
#include <evl/mutex.h>
#include <sys/eventfd.h>
#include "internal.h"
#define __MUTEX_ACTIVE_MAGIC 0xab12ab12
#define __MUTEX_DEAD_MAGIC 0
static int create_mutex(struct evl_mutex *mutex, int clockfd,
unsigned int ceiling, int flags)
{
int ret, fd, protocol, ptype;
pthread_mutexattr_t attr;
if (!eshi_is_initialized())
return -ENXIO;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
mutex->active.clock = CLOCK_MONOTONIC;
break;
case EVL_CLOCK_REALTIME:
mutex->active.clock = CLOCK_REALTIME;
break;
default:
return -EINVAL;
}
if (flags & EVL_MUTEX_RECURSIVE)
ptype = PTHREAD_MUTEX_RECURSIVE;
else
ptype = PTHREAD_MUTEX_NORMAL;
fd = eventfd(1, EFD_CLOEXEC); /* Set to always readable. */
if (fd < 0)
return -errno;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, ptype);
protocol = PTHREAD_PRIO_INHERIT;
if (ceiling) {
protocol = PTHREAD_PRIO_PROTECT;
pthread_mutexattr_setprioceiling(&attr, ceiling);
}
ret = pthread_mutexattr_setprotocol(&attr, protocol);
if (ret) {
pthread_mutexattr_destroy(&attr);
close(fd);
return -ret;
}
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
ret = pthread_mutex_init(&mutex->active.mutex, &attr);
pthread_mutexattr_destroy(&attr);
if (ret) {
close(fd);
return -ret;
}
mutex->active.fd = fd;
mutex->magic = __MUTEX_ACTIVE_MAGIC;
return 0;
}
int evl_create_mutex(struct evl_mutex *mutex,
int clockfd, unsigned int ceiling,
int flags, const char *fmt, ...)
{
return create_mutex(mutex, clockfd, ceiling, flags) ?:
mutex->active.fd;
}
static int check_sanity(struct evl_mutex *mutex)
{
int ret = 0;
if (mutex->magic == __MUTEX_UNINIT_MAGIC)
ret = create_mutex(mutex,
mutex->uninit.clockfd,
mutex->uninit.ceiling,
mutex->uninit.flags);
else if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
return ret;
}
int evl_lock_mutex(struct evl_mutex *mutex)
{
int ret;
ret = check_sanity(mutex);
if (ret)
return ret;
return -pthread_mutex_lock(&mutex->active.mutex);
}
int evl_timedlock_mutex(struct evl_mutex *mutex,
const struct timespec *timeout)
{
const struct timespec *tp = timeout;
struct timespec ts;
int ret;
ret = check_sanity(mutex);
if (ret)
return ret;
if (timeout->tv_sec < 0 || timeout->tv_nsec >= 1000000000L)
return -EINVAL;
if (mutex->active.clock == CLOCK_MONOTONIC) {
timespec_mono_to_real(&ts, timeout);
tp = &ts;
}
return -pthread_mutex_timedlock(&mutex->active.mutex, tp);
}
int evl_trylock_mutex(struct evl_mutex *mutex)
{
int ret;
ret = check_sanity(mutex);
if (ret)
return ret;
return -pthread_mutex_trylock(&mutex->active.mutex);
}
int evl_unlock_mutex(struct evl_mutex *mutex)
{
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
return -pthread_mutex_unlock(&mutex->active.mutex);
}
int evl_set_mutex_ceiling(struct evl_mutex *mutex,
unsigned int ceiling)
{
int old;
if (ceiling == 0)
return -EINVAL;
if (mutex->magic == __MUTEX_UNINIT_MAGIC) {
if (mutex->uninit.ceiling == 0)
return -EINVAL;
mutex->uninit.ceiling = ceiling;
}
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
return -pthread_mutex_setprioceiling(&mutex->active.mutex,
ceiling, &old);
}
int evl_get_mutex_ceiling(struct evl_mutex *mutex)
{
int ret, ceiling;
if (mutex->magic == __MUTEX_UNINIT_MAGIC)
return mutex->uninit.ceiling;
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
ret = pthread_mutex_getprioceiling(&mutex->active.mutex, &ceiling);
if (ret)
return ret == EINVAL ? 0 : -ret;
return ceiling;
}
int evl_close_mutex(struct evl_mutex *mutex)
{
if (mutex->magic == __MUTEX_UNINIT_MAGIC)
return 0;
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
close(mutex->active.fd);
mutex->magic = __MUTEX_DEAD_MAGIC;
return -pthread_mutex_destroy(&mutex->active.mutex);
}