eshi: EVL shim library

The EVL shim library mimics the behavior of the original EVL API based
on plain POSIX calls from the native *libc, which does not require the
EVL core to be enabled in the host kernel. It is useful when the
real-time guarantees delivered by the EVL core are not required for
quick prototyping or debugging application code.
This commit is contained in:
Philippe Gerum 2019-08-05 16:36:38 +02:00
parent ec669471c0
commit f052b6d31c
34 changed files with 2017 additions and 10 deletions

View File

@ -2,7 +2,7 @@
include config.mk
TARGETS := include lib tests benchmarks utils
TARGETS := include lib benchmarks utils eshi tests
$(MAIN_GOALS): output-Makefile
@for target in $(TARGETS); do \

53
eshi/Makefile Normal file
View File

@ -0,0 +1,53 @@
# SPDX-License-Identifier: MIT
include ../config.mk
include ../libversion.mk
SONAME := libeshi.so
DTSONAME := $(SONAME).$(EVL_IVERSION)
SOLIBNAME := libeshi-$(EVL_SERIAL).so.$(EVL_IVERSION)
ARLIBNAME := libeshi-$(EVL_SERIAL).a
TARGETS := $(O_DIR)/$(SOLIBNAME) $(O_DIR)/$(ARLIBNAME)
SRCFILES := $(wildcard *.c)
PIC_OBJFILES = $(SRCFILES:%.c=$(O_DIR)/%-pic.o)
OBJFILES = $(SRCFILES:%.c=$(O_DIR)/%.o)
DEPFILES = $(SRCFILES:%.c=$(O_DIR)/%.d)
LIB_CPPFLAGS := $(BASE_CPPFLAGS) \
-I. \
-I../include/eshi \
-I../include
LIB_CFLAGS := $(LIB_CPPFLAGS) $(BASE_CFLAGS)
override CFLAGS := $(LIB_CFLAGS) $(CFLAGS)
override LDFLAGS := $(LDFLAGS) -lpthread -lrt
all: output-Makefile $(TARGETS)
install: all
$(call inst-cmd,$(SOLIBNAME),$(INSTALL) -D $(O_DIR)/$(SOLIBNAME) $(DESTDIR)/$(libdir)/$(SOLIBNAME))
@$(LN_S) $(SOLIBNAME) $(DESTDIR)/$(libdir)/$(DTSONAME)
@$(LN_S) $(DTSONAME) $(DESTDIR)/$(libdir)/$(SONAME)
$(call inst-cmd,$(ARLIBNAME),$(INSTALL) -D $(O_DIR)/$(ARLIBNAME) $(DESTDIR)/$(libdir)/$(ARLIBNAME))
clean clobber mrproper: output-Makefile
$(Q)$(RM) -f $(PIC_OBJFILES) $(OBJFILES) $(TARGETS) $(O_DIR)/$(DTSONAME) $(DEPFILES)
$(O_DIR)/$(SOLIBNAME): $(PIC_OBJFILES)
$(call ld-cmd,$@,$(CC) -shared -Wl$(comma)-soname$(comma)$(DTSONAME) -o $(@) \
$(PIC_OBJFILES) $(LDFLAGS) -Wl$(comma)-rpath=$(libdir) -Wl$(comma)--export-dynamic)
$(Q)$(LN_S) $(SOLIBNAME) $(O_DIR)/$(DTSONAME)
$(O_DIR)/$(ARLIBNAME): $(OBJFILES)
$(call ar-cmd,$@,$(AR) ru $@ $(OBJFILES))
$(O_DIR)/%-pic.o: %.c
$(call cc-pic-cmd,$@,$(CC) $(CFLAGS) -fPIC -c -o $@ $<)
$(O_DIR)/%.o: %.c
$(call cc-cmd,$@,$(CC) $(CFLAGS) -c -o $@ $<)
-include $(DEPFILES)

47
eshi/README.md Normal file
View File

@ -0,0 +1,47 @@
== PURPOSE of libeshi
The EVL shim library mimics the behavior of the original EVL API based
on plain POSIX calls from the native *libc, which does not require the
EVL core to be enabled in the host kernel. It is useful when the
real-time guarantees delivered by the EVL core are not required for
quick prototyping or debugging application code.
== USAGE
To build against the EVL shim library, you need to update a couple of
settings from the Makefile building your application as follows (*):
CPPFLAGS/CFLAGS:
- Replace -I$(prefix)/include by -I$(prefix)/include/eshi
LDFLAGS:
- Replace -levl by -leshi
For instance:
$(CC) -o app app.c -I$(prefix)/include/eshi -L$(prefix)/lib -leshi -lpthread -lrt
(*) $(prefix) is the installation root of libevl (e.g. /usr/evl).
== LIMITATIONS
This API supports process-local services only. The resources/objects
it creates cannot be shared between processes.
The following calls are not supported:
evl_peek_flags
evl_peek_sem
evl_new_xbuf
evl_open_mutex
evl_open_event
evl_open_flags
evl_open_sem
== VARIATION(S)
evl_poll: POLLOUT is always set for evl_flags. Unlike with the native
libevl API, this condition cannot be monitored for detecting event
consumption by the receiver side, i.e. waiting for the value to be
cleared upon a successful call to evl_wait*_flags.

77
eshi/clock.c Normal file
View File

@ -0,0 +1,77 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <time.h>
#include <evl/clock.h>
int evl_set_clock(int clockfd, const struct timespec *tp)
{
int ret;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
case EVL_CLOCK_REALTIME:
ret = clock_settime(-clockfd, tp);
if (ret)
return -errno;
break;
default:
return -EINVAL;
}
return 0;
}
int evl_get_clock_resolution(int clockfd, struct timespec *res)
{
int ret;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
case EVL_CLOCK_REALTIME:
ret = clock_getres(-clockfd, res);
if (ret)
return -errno;
break;
default:
return -EINVAL;
}
return 0;
}
int evl_sleep(int clockfd, const struct timespec *timeout)
{
int ret;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
case EVL_CLOCK_REALTIME:
ret = clock_nanosleep(-clockfd, TIMER_ABSTIME, timeout, NULL);
if (ret)
return -errno;
break;
default:
return -EINVAL;
}
return 0;
}
int evl_udelay(unsigned int usecs)
{
struct timespec delay;
int ret;
delay.tv_sec = usecs / 1000000U;
delay.tv_nsec = (usecs % 1000000U) * 1000U;
ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &delay, NULL);
if (ret)
return -errno;
return 0;
}

130
eshi/event.c Normal file
View File

@ -0,0 +1,130 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <unistd.h>
#include <evl/event.h>
#include <sys/eventfd.h>
#include "internal.h"
#define __EVENT_ACTIVE_MAGIC 0xef55ef55
#define __EVENT_DEAD_MAGIC 0
int evl_new_event(struct evl_event *evt, int clockfd,
const char *fmt, ...)
{
pthread_condattr_t attr;
int ret, fd;
if (!eshi_is_initialized())
return -ENXIO;
if (clockfd != EVL_CLOCK_MONOTONIC &&
clockfd != EVL_CLOCK_REALTIME)
return -EINVAL;
fd = eventfd(0, EFD_CLOEXEC);
if (fd < 0)
return -errno;
pthread_condattr_init(&attr);
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE);
pthread_condattr_setclock(&attr, -clockfd);
ret = pthread_cond_init(&evt->active.cond, &attr);
pthread_condattr_destroy(&attr);
if (ret) {
close(fd);
return -ret;
}
evt->active.fd = fd;
evt->magic = __EVENT_ACTIVE_MAGIC;
return fd;
}
static int check_sanity(struct evl_event *evt)
{
int ret = 0, fd;
if (evt->magic == __EVENT_UNINIT_MAGIC) {
fd = evl_new_event(evt,
evt->uninit.clockfd,
evt->uninit.name);
return fd < 0 ? fd : 0;
} else if (evt->magic != __EVENT_ACTIVE_MAGIC)
return -EINVAL;
return ret;
}
int evl_wait_event(struct evl_event *evt, struct evl_mutex *mutex)
{
int ret;
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
ret = check_sanity(evt);
if (ret)
return ret;
return -pthread_cond_wait(&evt->active.cond,
&mutex->active.mutex);
}
int evl_timedwait_event(struct evl_event *evt,
struct evl_mutex *mutex,
const struct timespec *timeout)
{
int ret;
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
ret = check_sanity(evt);
if (ret)
return ret;
return -pthread_cond_timedwait(&evt->active.cond,
&mutex->active.mutex, timeout);
}
int evl_signal_event(struct evl_event *evt)
{
int ret;
ret = check_sanity(evt);
if (ret)
return ret;
return -pthread_cond_signal(&evt->active.cond);
}
int evl_broadcast_event(struct evl_event *evt)
{
int ret;
ret = check_sanity(evt);
if (ret)
return ret;
return -pthread_cond_broadcast(&evt->active.cond);
}
int evl_close_event(struct evl_event *evt)
{
if (evt->magic == __EVENT_UNINIT_MAGIC)
return 0;
if (evt->magic != __EVENT_ACTIVE_MAGIC)
return -EINVAL;
close(evt->active.fd);
evt->magic = __EVENT_DEAD_MAGIC;
return -pthread_cond_destroy(&evt->active.cond);
}

189
eshi/flags.c Normal file
View File

@ -0,0 +1,189 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <poll.h>
#include <fcntl.h>
#include <evl/flags.h>
#include <sys/eventfd.h>
#include "internal.h"
#define __FLAGS_ACTIVE_MAGIC 0xb42bb42b
#define __FLAGS_DEAD_MAGIC 0
int evl_new_flags(struct evl_flags *flg, int clockfd, int initval,
const char *fmt, ...)
{
int fd;
if (!eshi_is_initialized())
return -ENXIO;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
flg->active.clock = CLOCK_MONOTONIC;
break;
case EVL_CLOCK_REALTIME:
flg->active.clock = CLOCK_REALTIME;
break;
default:
return -EINVAL;
}
fd = eventfd(initval, EFD_CLOEXEC | EFD_NONBLOCK);
if (fd < 0)
return -errno;
flg->active.fd = fd;
flg->magic = __FLAGS_ACTIVE_MAGIC;
return fd;
}
int evl_close_flags(struct evl_flags *flg)
{
if (flg->magic == __FLAGS_UNINIT_MAGIC)
return 0;
if (flg->magic != __FLAGS_ACTIVE_MAGIC)
return -EINVAL;
close(flg->active.fd);
flg->magic = __FLAGS_DEAD_MAGIC;
return 0;
}
static int check_sanity(struct evl_flags *flg)
{
int efd;
if (flg->magic == __FLAGS_UNINIT_MAGIC) {
efd = evl_new_flags(flg,
flg->uninit.clockfd,
flg->uninit.initval,
flg->uninit.name);
return efd < 0 ? efd : 0;
}
return flg->magic != __FLAGS_ACTIVE_MAGIC ? -EINVAL : 0;
}
static int timedwait_flags(struct evl_flags *flg,
const struct timespec *timeout,
int *r_bits)
{
const struct timespec *tp = timeout;
struct timespec ts, now;
struct pollfd pollfd;
uint64_t val;
int ret;
if (!tp || (tp->tv_sec == 0 && tp->tv_nsec == 0))
goto poll;
for (;;) {
if (tp) {
clock_gettime(flg->active.clock, &now);
ts = *timeout;
timespec_sub(&ts, &now);
if (ts.tv_sec < 0) {
ts.tv_sec = 0;
ts.tv_nsec = 0;
}
tp = &ts;
}
poll:
pollfd.fd = flg->active.fd;
pollfd.events = POLLIN;
pollfd.revents = 0;
ret = ppoll(&pollfd, 1, tp, NULL);
if (ret < 0)
return -errno;
if (ret == 0)
break;
if (!(pollfd.revents & POLLIN))
return -EINVAL;
ret = read(flg->active.fd, &val, sizeof(val));
if (ret > 0) {
*r_bits = (unsigned int)(val & ~0);
return 0;
}
if (errno != -EAGAIN)
return -errno;
}
return -ETIMEDOUT;
}
int evl_timedwait_flags(struct evl_flags *flg,
const struct timespec *timeout,
int *r_bits)
{
int ret;
if (timeout == NULL)
return -EINVAL;
if (timeout->tv_sec < 0 || timeout->tv_nsec >= 1000000000L)
return -EINVAL;
ret = check_sanity(flg);
if (ret)
return ret;
return timedwait_flags(flg, timeout, r_bits);
}
int evl_wait_flags(struct evl_flags *flg, int *r_bits)
{
int ret;
ret = check_sanity(flg);
if (ret)
return ret;
return timedwait_flags(flg, NULL, r_bits);
}
int evl_trywait_flags(struct evl_flags *flg, int *r_bits)
{
struct timespec zerotime = { .tv_sec = 0, .tv_nsec = 0};
int ret;
ret = check_sanity(flg);
if (ret)
return ret;
ret = timedwait_flags(flg, &zerotime, r_bits);
if (ret == -ETIMEDOUT)
return -EAGAIN;
return ret;
}
int evl_post_flags(struct evl_flags *flg, int bits)
{
uint64_t val = (uint64_t)(unsigned int)bits;
int ret;
ret = check_sanity(flg);
if (ret)
return ret;
ret = write(flg->active.fd, &val, sizeof(val));
if (ret != sizeof(val))
return -errno;
return 0;
}

7
eshi/heap.c Normal file
View File

@ -0,0 +1,7 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include "../lib/heap.c"

45
eshi/init.c Normal file
View File

@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <stdbool.h>
#include <errno.h>
#include <pthread.h>
#include <evl/evl.h>
#include "internal.h"
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static int init_status = -ENXIO;
static void atfork_handler(void)
{
init_once = PTHREAD_ONCE_INIT;
init_status = 0;
}
static inline int do_init(void)
{
pthread_atfork(NULL, NULL, atfork_handler);
return eshi_init_threads();
}
static void do_init_once(void)
{
init_status = do_init();
}
int evl_init(void)
{
pthread_once(&init_once, do_init_once);
return init_status;
}
bool eshi_is_initialized(void)
{
return init_status == 0;
}

55
eshi/internal.h Normal file
View File

@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_INTERNAL_H
#define _EVL_ESHI_INTERNAL_H
#include <stdbool.h>
#include <time.h>
#include <pthread.h>
#include <stdio.h>
static inline
void timespec_add(struct timespec *r, const struct timespec *t)
{
r->tv_sec += t->tv_sec;
r->tv_nsec += t->tv_nsec;
if (r->tv_nsec >= 1000000000) {
r->tv_sec++;
r->tv_nsec -= 1000000000;
}
}
static inline
void timespec_sub(struct timespec *r, const struct timespec *t)
{
r->tv_sec -= t->tv_sec;
r->tv_nsec -= t->tv_nsec;
if (r->tv_nsec < 0) {
r->tv_sec--;
r->tv_nsec += 1000000000;
}
}
static inline
void timespec_mono_to_real(struct timespec *r, const struct timespec *t)
{
struct timespec now;
*r = *t;
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_sub(r, &now);
clock_gettime(CLOCK_REALTIME, &now);
timespec_add(r, &now);
}
pthread_t eshi_find_thread_by_fd(int fd);
int eshi_init_threads(void);
bool eshi_is_initialized(void);
#endif /* _EVL_ESHI_INTERNAL_H */

197
eshi/mutex.c Normal file
View File

@ -0,0 +1,197 @@
/*
* 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, int ceiling)
{
pthread_mutexattr_t attr;
int ret, fd, protocol;
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;
}
fd = eventfd(1, EFD_CLOEXEC); /* Set to always readable. */
if (fd < 0)
return -errno;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
protocol = PTHREAD_PRIO_INHERIT;
if (ceiling) {
protocol = PTHREAD_PRIO_PROTECT;
pthread_mutexattr_setprioceiling(&attr, ceiling);
}
pthread_mutexattr_setprotocol(&attr, protocol);
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_new_mutex(struct evl_mutex *mutex,
int clockfd, const char *fmt, ...)
{
return create_mutex(mutex, clockfd, 0) ?:
mutex->active.fd;
}
int evl_new_mutex_ceiling(struct evl_mutex *mutex,
int clockfd, unsigned int ceiling,
const char *fmt, ...)
{
return create_mutex(mutex, clockfd, ceiling) ?:
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);
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) {
if (mutex->uninit.ceiling == 0)
return -EINVAL;
return mutex->uninit.ceiling;
}
if (mutex->magic != __MUTEX_ACTIVE_MAGIC)
return -EINVAL;
ret = pthread_mutex_getprioceiling(&mutex->active.mutex, &ceiling);
if (ret)
return -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);
}

125
eshi/poll.c Normal file
View File

@ -0,0 +1,125 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <evl/poll.h>
#include "internal.h"
int evl_new_poll(void)
{
return epoll_create1(EPOLL_CLOEXEC);
}
int evl_add_pollfd(int efd, int newfd, unsigned int events)
{
struct epoll_event ev;
int ret;
if (efd == newfd)
return -ELOOP;
ev.events = events;
ev.data.fd = newfd;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, newfd, &ev);
if (ret)
return -errno;
return 0;
}
int evl_del_pollfd(int efd, int delfd)
{
int ret;
ret = epoll_ctl(efd, EPOLL_CTL_DEL, delfd, NULL);
if (ret)
return -errno;
return 0;
}
int evl_mod_pollfd(int efd, int modfd, unsigned int events)
{
struct epoll_event ev;
int ret;
ev.events = events;
ev.data.fd = modfd;
ret = epoll_ctl(efd, EPOLL_CTL_MOD, modfd, &ev);
if (ret)
return -errno;
return 0;
}
/*
* Use a fast stack-based array for monitoring a small number of
* events.
*/
#define FAST_EVENT_NR 8
static int do_timedpoll(int efd, struct evl_poll_event *pollset,
int nrset, int msecs)
{
struct epoll_event fast_evs[FAST_EVENT_NR], *evs = fast_evs;
int ret, n;
if (nrset == 0)
return 0;
if (nrset < 0)
return -EINVAL;
if (nrset > FAST_EVENT_NR) {
evs = malloc(sizeof(*evs) * nrset);
if (evs == NULL)
return -ENOMEM;
}
ret = epoll_wait(efd, evs, nrset, msecs);
if (ret < 0) {
ret = -errno;
goto out;
}
if (ret == 0) {
ret = -ETIMEDOUT;
goto out;
}
for (n = 0; n < nrset; n++) {
pollset[n].fd = evs[n].data.fd;
pollset[n].events = evs[n].events;
}
out:
if (evs != fast_evs)
free(evs);
return ret;
}
int evl_timedpoll(int efd, struct evl_poll_event *pollset,
int nrset, struct timespec *timeout)
{
int msecs;
if (timeout->tv_sec < 0 || timeout->tv_nsec >= 1000000000L)
return -EINVAL;
msecs = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
return do_timedpoll(efd, pollset, nrset, msecs);
}
int evl_poll(int efd, struct evl_poll_event *pollset, int nrset)
{
return do_timedpoll(efd, pollset, nrset, -1);
}

34
eshi/proxy.c Normal file
View File

@ -0,0 +1,34 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <evl/thread.h>
#include <evl/proxy.h>
static __thread __attribute__ ((tls_model (EVL_TLS_MODEL)))
char fmt_buf[1024];
int evl_vprint_proxy(int proxyfd, const char *fmt, va_list ap)
{
ssize_t len = vsnprintf(fmt_buf, sizeof(fmt_buf), fmt, ap);
return write(proxyfd, fmt_buf, len);
}
int evl_print_proxy(int proxyfd, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = evl_vprint_proxy(proxyfd, fmt, ap);
va_end(ap);
return ret;
}

49
eshi/sched.c Normal file
View File

@ -0,0 +1,49 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <sched.h>
#include <evl/sched.h>
#include "internal.h"
int evl_set_schedattr(int efd, const struct evl_sched_attrs *attrs)
{
int policy = attrs->sched_policy;
struct sched_param param;
pthread_t thread;
if (policy != SCHED_OTHER && policy != SCHED_FIFO)
return -EINVAL;
thread = eshi_find_thread_by_fd(efd);
if (!thread)
return -EBADF;
param.sched_priority = attrs->sched_priority;
return -pthread_setschedparam(thread, policy, &param);
}
int evl_get_schedattr(int efd, struct evl_sched_attrs *attrs)
{
int policy = attrs->sched_policy;
struct sched_param param;
pthread_t thread;
int ret;
thread = eshi_find_thread_by_fd(efd);
if (!thread)
return -EBADF;
ret = pthread_getschedparam(thread, &policy, &param);
if (ret)
return -ret;
attrs->sched_policy = policy;
attrs->sched_priority = param.sched_priority;
return 0;
}

184
eshi/sem.c Normal file
View File

@ -0,0 +1,184 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <evl/sem.h>
#include <sys/eventfd.h>
#include "internal.h"
#define __SEM_ACTIVE_MAGIC 0xcb13cb13
#define __SEM_DEAD_MAGIC 0
int evl_new_sem(struct evl_sem *sem, int clockfd, int initval,
const char *fmt, ...)
{
int fd;
/*
* evl_init() must have run: exclusively for proper emulation
* of libevl.
*/
if (!eshi_is_initialized())
return -ENXIO;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
sem->active.clock = CLOCK_MONOTONIC;
break;
case EVL_CLOCK_REALTIME:
sem->active.clock = CLOCK_REALTIME;
break;
default:
return -EINVAL;
}
fd = eventfd(initval, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC);
if (fd < 0)
return -errno;
sem->active.fd = fd;
sem->magic = __SEM_ACTIVE_MAGIC;
return fd;
}
int evl_close_sem(struct evl_sem *sem)
{
if (sem->magic == __SEM_UNINIT_MAGIC)
return 0;
if (sem->magic != __SEM_ACTIVE_MAGIC)
return -EINVAL;
close(sem->active.fd);
sem->magic = __SEM_DEAD_MAGIC;
return 0;
}
static int check_sanity(struct evl_sem *sem)
{
int efd;
if (sem->magic == __SEM_UNINIT_MAGIC) {
efd = evl_new_sem(sem,
sem->uninit.clockfd,
sem->uninit.initval,
sem->uninit.name);
return efd < 0 ? efd : 0;
}
return sem->magic != __SEM_ACTIVE_MAGIC ? -EINVAL : 0;
}
static int timedget_sem(struct evl_sem *sem,
const struct timespec *timeout)
{
const struct timespec *tp = timeout;
struct timespec ts, now;
struct pollfd pollfd;
uint64_t val;
int ret;
if (!tp || (tp->tv_sec == 0 && tp->tv_nsec == 0))
goto poll;
for (;;) {
if (tp) {
clock_gettime(sem->active.clock, &now);
ts = *timeout;
timespec_sub(&ts, &now);
if (ts.tv_sec < 0) {
ts.tv_sec = 0;
ts.tv_nsec = 0;
}
tp = &ts;
}
poll:
pollfd.fd = sem->active.fd;
pollfd.events = POLLIN;
pollfd.revents = 0;
ret = ppoll(&pollfd, 1, tp, NULL);
if (ret < 0)
return -errno;
if (ret == 0)
break;
if (!(pollfd.revents & POLLIN))
return -EINVAL;
ret = read(sem->active.fd, &val, sizeof(val));
if (ret > 0)
return 0;
if (errno != -EAGAIN)
return -errno;
}
return -ETIMEDOUT;
}
int evl_timedget_sem(struct evl_sem *sem,
const struct timespec *timeout)
{
int ret;
if (timeout == NULL)
return -EINVAL;
ret = check_sanity(sem);
if (ret)
return ret;
return timedget_sem(sem, timeout);
}
int evl_get_sem(struct evl_sem *sem)
{
int ret;
ret = check_sanity(sem);
if (ret)
return ret;
return timedget_sem(sem, NULL);
}
int evl_tryget_sem(struct evl_sem *sem)
{
struct timespec zerotime = { .tv_sec = 0, .tv_nsec = 0 };
int ret;
ret = check_sanity(sem);
if (ret)
return ret;
ret = timedget_sem(sem, &zerotime);
if (ret == -ETIMEDOUT)
return -EAGAIN;
return ret;
}
int evl_put_sem(struct evl_sem *sem)
{
uint64_t val = (uint64_t)1U;
int ret;
ret = check_sanity(sem);
if (ret)
return ret;
ret = write(sem->active.fd, &val, sizeof(val));
if (ret != sizeof(val))
return -errno;
return 0;
}

107
eshi/thread.c Normal file
View File

@ -0,0 +1,107 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <unistd.h>
#include <search.h>
#include <pthread.h>
#include <evl/evl.h>
#include <evl/thread.h>
#include <sys/eventfd.h>
#include "internal.h"
static __thread __attribute__ ((tls_model (EVL_TLS_MODEL)))
int evl_efd = -1;
static __thread __attribute__ ((tls_model (EVL_TLS_MODEL)))
struct evl_tsd {
int fd;
pthread_t thread;
} evl_tsd;
static pthread_key_t tsd_key;
static void *fdtree_root;
static int compare_nodes(const void *l, const void *r)
{
const struct evl_tsd *lt = l, *rt = r;
return lt->fd - rt->fd;
}
int evl_attach_self(const char *fmt, ...)
{
struct evl_tsd **node;
int ret, fd;
if (evl_efd != -1)
return -EBUSY;
ret = evl_init();
if (ret)
return ret;
fd = eventfd(0, EFD_CLOEXEC);
if (fd < 0)
return -errno;
evl_tsd.fd = fd;
evl_tsd.thread = pthread_self();
node = (struct evl_tsd **)tsearch(&evl_tsd, &fdtree_root, compare_nodes);
if (!node) {
close(fd);
return -ENOMEM;
}
if ((*node)->fd != fd)
return -EBUSY; /* Weird. */
pthread_setspecific(tsd_key, &evl_tsd);
evl_efd = fd;
return fd;
}
int evl_detach_self(void)
{
if (evl_efd < 0)
return -EPERM;
pthread_setspecific(tsd_key, NULL);
tdelete(&evl_tsd, &fdtree_root, compare_nodes);
close(evl_efd);
evl_efd = -1;
return 0;
}
int evl_get_self(void)
{
return evl_efd;
}
pthread_t eshi_find_thread_by_fd(int fd)
{
struct evl_tsd tsd = {
.fd = fd,
}, **node;
node = tsearch(&tsd, &fdtree_root, compare_nodes);
if (!node)
return (pthread_t)NULL;
return (*node)->thread;
}
static void unregister_thread(void *p)
{
evl_detach_self();
}
int eshi_init_threads(void)
{
return -pthread_key_create(&tsd_key, unregister_thread);
}

58
eshi/timer.c Normal file
View File

@ -0,0 +1,58 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <time.h>
#include <sys/timerfd.h>
#include <evl/clock.h>
#include <evl/timer.h>
int evl_new_timer(int clockfd)
{
clockid_t clk;
int fd;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
clk = CLOCK_MONOTONIC;
break;
case EVL_CLOCK_REALTIME:
clk = CLOCK_REALTIME;
break;
default:
return -EINVAL;
}
fd = timerfd_create(clk, TFD_CLOEXEC);
if (fd < 0)
return -errno;
return fd;
}
int evl_set_timer(int efd,
struct itimerspec *value,
struct itimerspec *ovalue)
{
int ret;
ret = timerfd_settime(efd, TFD_TIMER_ABSTIME, value, ovalue);
if (ret)
return -errno;
return 0;
}
int evl_get_timer(int efd, struct itimerspec *value)
{
int ret;
ret = timerfd_gettime(efd, value);
if (ret)
return -errno;
return 0;
}

View File

@ -33,3 +33,5 @@ install: all
$(Q)$(MKDIR_P) $(DESTDIR)/$(includedir)/uapi
$(call inst-cmd,uapi-headers,cd $(O_UAPI) && find -L evl \! \( -name '*~' \) -type f | $(CPIO) -Lpdum --quiet $(DESTDIR)/$(includedir)/uapi)
$(call inst-cmd,interface-headers,find evl \! \( -name '*~' \) -type f | $(CPIO) -Lpdum --quiet $(DESTDIR)/$(includedir))
$(call inst-cmd,eshi-headers,find eshi \! \( -name '*~' \) -type f | $(CPIO) -Lpdum --quiet $(DESTDIR)/$(includedir))
$(call inst-cmd,eshi-headers,$(CP) evl/atomic.h evl/list.h evl/heap.h $(DESTDIR)/$(includedir)/eshi/evl)

53
include/eshi/evl/clock.h Normal file
View File

@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_CLOCK_H
#define _EVL_ESHI_CLOCK_H
#include <time.h>
#include <errno.h>
#include <evl/syscall.h>
#define EVL_CLOCK_MONOTONIC (-CLOCK_MONOTONIC)
#define EVL_CLOCK_REALTIME (-CLOCK_REALTIME)
#ifdef __cplusplus
extern "C" {
#endif
static inline
int evl_read_clock(int clockfd, struct timespec *tp)
{
int ret;
switch (clockfd) {
case EVL_CLOCK_MONOTONIC:
case EVL_CLOCK_REALTIME:
break;
default:
return -EINVAL;
}
ret = clock_gettime(-clockfd, tp);
if (ret)
return -errno;
return 0;
}
int evl_set_clock(int clockfd, const struct timespec *tp);
int evl_get_clock_resolution(int clockfd, struct timespec *tp);
int evl_sleep(int clockfd, const struct timespec *timeout);
int evl_udelay(unsigned int usecs);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_CLOCK_H */

67
include/eshi/evl/event.h Normal file
View File

@ -0,0 +1,67 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_EVENT_H
#define _EVL_ESHI_EVENT_H
#include <time.h>
#include <errno.h>
#include <evl/atomic.h>
#include <evl/mutex.h>
struct evl_event {
unsigned int magic;
union {
struct {
int fd;
pthread_cond_t cond;
} active;
struct {
const char *name;
int clockfd;
} uninit;
};
};
#define __EVENT_UNINIT_MAGIC 0x01770177
#define EVL_EVENT_INITIALIZER(__name, __clockfd) { \
.magic = __EVENT_UNINIT_MAGIC, \
.uninit = { \
.name = (__name), \
.clockfd = (__clockfd), \
} \
}
#ifdef __cplusplus
extern "C" {
#endif
int evl_new_event(struct evl_event *evt,
int clockfd,
const char *fmt, ...);
int evl_open_event(struct evl_event *evt,
const char *fmt, ...);
int evl_wait_event(struct evl_event *evt,
struct evl_mutex *mutex);
int evl_timedwait_event(struct evl_event *evt,
struct evl_mutex *mutex,
const struct timespec *timeout);
int evl_signal_event(struct evl_event *evt);
int evl_broadcast_event(struct evl_event *evt);
int evl_close_event(struct evl_event *evt);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_EVENT_H */

24
include/eshi/evl/evl.h Normal file
View File

@ -0,0 +1,24 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_EVL_H
#define _EVL_ESHI_EVL_H
#include <evl/syscall.h>
#define __EVL__ 5 /* API revision */
#ifdef __cplusplus
extern "C" {
#endif
int evl_init(void);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_EVL_H */

69
include/eshi/evl/flags.h Normal file
View File

@ -0,0 +1,69 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_FLAGS_H
#define _EVL_ESHI_FLAGS_H
#include <time.h>
#include <evl/clock.h>
struct evl_flags {
unsigned int magic;
union {
struct {
clockid_t clock;
int fd;
} active;
struct {
const char *name;
int clockfd;
int initval;
} uninit;
};
};
#define __FLAGS_UNINIT_MAGIC 0xfebcfebc
#define EVL_FLAGS_INITIALIZER(__name, __clockfd, __initval) { \
.magic = __FLAGS_UNINIT_MAGIC, \
.uninit = { \
.name = (__name), \
.clockfd = (__clockfd), \
.initval = (__initval), \
} \
}
#ifdef __cplusplus
extern "C" {
#endif
int evl_new_flags(struct evl_flags *flg,
int clockfd, int initval,
const char *fmt, ...);
int evl_open_flags(struct evl_flags *flg,
const char *fmt, ...);
int evl_close_flags(struct evl_flags *flg);
int evl_wait_flags(struct evl_flags *flg,
int *r_bits);
int evl_timedwait_flags(struct evl_flags *flg,
const struct timespec *timeout,
int *r_bits);
int evl_post_flags(struct evl_flags *flg,
int bits);
int evl_trywait_flags(struct evl_flags *flg,
int *r_bits);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_FLAGS_H */

82
include/eshi/evl/mutex.h Normal file
View File

@ -0,0 +1,82 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_MUTEX_H
#define _EVL_ESHI_MUTEX_H
#include <time.h>
#include <pthread.h>
#include <evl/clock.h>
struct evl_mutex {
unsigned int magic;
union {
struct {
pthread_mutex_t mutex;
clockid_t clock;
int fd;
} active;
struct {
const char *name;
int clockfd;
unsigned int ceiling;
} uninit;
};
};
#define __MUTEX_ACTIVE_MAGIC 0xab12ab12
#define __MUTEX_UNINIT_MAGIC 0xfe11fe11
#define EVL_MUTEX_INITIALIZER(__name, __clockfd) { \
.magic = __MUTEX_UNINIT_MAGIC, \
.uninit = { \
.name = (__name), \
.clockfd = (__clockfd), \
.ceiling = 0, \
} \
}
#define EVL_MUTEX_CEILING_INITIALIZER(__name, __clockfd, __ceiling) { \
.magic = __MUTEX_UNINIT_MAGIC, \
.uninit = { \
.name = (__name), \
.clockfd = (__clockfd), \
.ceiling = (__ceiling), \
} \
}
#ifdef __cplusplus
extern "C" {
#endif
int evl_new_mutex(struct evl_mutex *mutex,
int clockfd, const char *fmt, ...);
int evl_new_mutex_ceiling(struct evl_mutex *mutex,
int clockfd, unsigned int ceiling,
const char *fmt, ...);
int evl_lock_mutex(struct evl_mutex *mutex);
int evl_timedlock_mutex(struct evl_mutex *mutex,
const struct timespec *timeout);
int evl_trylock_mutex(struct evl_mutex *mutex);
int evl_unlock_mutex(struct evl_mutex *mutex);
int evl_set_mutex_ceiling(struct evl_mutex *mutex,
unsigned int ceiling);
int evl_get_mutex_ceiling(struct evl_mutex *mutex);
int evl_close_mutex(struct evl_mutex *mutex);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_MUTEX_H */

43
include/eshi/evl/poll.h Normal file
View File

@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_POLL_H
#define _EVL_ESHI_POLL_H
#include <linux/types.h>
#include <sys/poll.h>
#include <evl/uapi.h>
struct evl_poll_event {
__u32 fd;
__u32 events;
};
#ifdef __cplusplus
extern "C" {
#endif
int evl_new_poll(void);
int evl_add_pollfd(int efd, int newfd,
unsigned int events);
int evl_del_pollfd(int efd, int delfd);
int evl_mod_pollfd(int efd, int modfd,
unsigned int events);
int evl_timedpoll(int efd, struct evl_poll_event *pollset,
int nrset, struct timespec *timeout);
int evl_poll(int efd, struct evl_poll_event *pollset,
int nrset);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_POLL_H */

48
include/eshi/evl/proxy.h Normal file
View File

@ -0,0 +1,48 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_PROXY_H
#define _EVL_ESHI_PROXY_H
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <evl/uapi.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline
int evl_new_proxy(int fd, size_t bufsz, size_t granularity,
const char *fmt, ...)
{
return fd;
}
static inline
ssize_t evl_send_proxy(int proxyfd, const void *buf, size_t len)
{
return write(proxyfd, buf, len);
}
int evl_vprint_proxy(int proxyfd,
const char *fmt, va_list ap);
int evl_print_proxy(int proxyfd,
const char *fmt, ...);
#define evl_printf(__fmt, __args...) printf(__fmt, ##__args)
#define proxy_outfd fileno(stdout)
#define proxy_errfd fileno(stderr)
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_PROXY_H */

27
include/eshi/evl/sched.h Normal file
View File

@ -0,0 +1,27 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_SCHED_H
#define _EVL_ESHI_SCHED_H
struct evl_sched_attrs {
int sched_policy;
int sched_priority;
};
#ifdef __cplusplus
extern "C" {
#endif
int evl_set_schedattr(int efd, const struct evl_sched_attrs *attrs);
int evl_get_schedattr(int efd, struct evl_sched_attrs *attrs);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_SCHED_H */

62
include/eshi/evl/sem.h Normal file
View File

@ -0,0 +1,62 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_SEM_H
#define _EVL_ESHI_SEM_H
#include <time.h>
#include <evl/clock.h>
struct evl_sem {
unsigned int magic;
union {
struct {
clockid_t clock;
int fd;
} active;
struct {
const char *name;
int clockfd;
int initval;
} uninit;
};
};
#define __SEM_UNINIT_MAGIC 0xed15ed15
#define EVL_SEM_INITIALIZER(__name, __clockfd, __initval) { \
.magic = __SEM_UNINIT_MAGIC, \
.uninit = { \
.name = (__name), \
.clockfd = (__clockfd), \
.initval = (__initval), \
} \
}
#ifdef __cplusplus
extern "C" {
#endif
int evl_new_sem(struct evl_sem *sem,
int clockfd, int initval,
const char *fmt, ...);
int evl_close_sem(struct evl_sem *sem);
int evl_get_sem(struct evl_sem *sem);
int evl_timedget_sem(struct evl_sem *sem,
const struct timespec *timeout);
int evl_put_sem(struct evl_sem *sem);
int evl_tryget_sem(struct evl_sem *sem);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_SEM_H */

View File

@ -0,0 +1,35 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_SYSCALL_H
#define _EVL_ESHI_SYSCALL_H
#include <sys/types.h>
#include <unistd.h>
#include <evl/uapi.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline ssize_t oob_read(int efd, void *buf, size_t count)
{
return read(efd, buf, count);
}
static inline ssize_t oob_write(int efd, const void *buf, size_t count)
{
return write(efd, buf, count);
}
#define oob_ioctl(__efd, __request, __args...) \
ioctl(__efd, __request, ##__args)
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_SYSCALL_H */

55
include/eshi/evl/thread.h Normal file
View File

@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_THREAD_H
#define _EVL_ESHI_THREAD_H
#include <limits.h>
#include <errno.h>
#include <stdbool.h>
#include <evl/syscall.h>
/* Enable dlopen() on libeshi.so. */
#define EVL_TLS_MODEL "global-dynamic"
#define EVL_STACK_DEFAULT \
({ \
int __ret = PTHREAD_STACK_MIN; \
if (__ret < 65536) \
__ret = 65536; \
__ret; \
})
#ifdef __cplusplus
extern "C" {
#endif
static inline bool evl_is_inband(void)
{
return true;
}
static inline int evl_switch_oob(void)
{
return -ENOSYS;
}
static inline int evl_switch_inband(void)
{
return 0;
}
int evl_attach_self(const char *fmt, ...);
int evl_detach_self(void);
int evl_get_self(void);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_THREAD_H */

30
include/eshi/evl/timer.h Normal file
View File

@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_TIMER_H
#define _EVL_ESHI_TIMER_H
#include <linux/types.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
int evl_new_timer(int clockfd);
int evl_set_timer(int efd,
struct itimerspec *value,
struct itimerspec *ovalue);
int evl_get_timer(int efd,
struct itimerspec *value);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_ESHI_TIMER_H */

19
include/eshi/evl/uapi.h Normal file
View File

@ -0,0 +1,19 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_ESHI_UAPI_H
#define _EVL_ESHI_UAPI_H
#define EVL_CLOCK_MONOTONIC_DEV "monotonic"
#define EVL_CLOCK_REALTIME_DEV "realtime"
#define EVL_CLOCK_DEV "clock"
#define EVL_MONITOR_DEV "monitor"
#define EVL_POLL_DEV "poll"
#define EVL_PROXY_DEV "proxy"
#define EVL_THREAD_DEV "thread"
#define EVL_XBUF_DEV "xbuf"
#endif /* _EVL_ESHI_UAPI_H */

View File

@ -8,7 +8,8 @@ HELPSRC := helpers.c
HELPOBJ := $(O_DIR)/helpers.o
TESTSRC := $(filter-out $(HELPSRC),$(ALLSRC))
FPSRC := fault.c
TARGETS = $(TESTSRC:%.c=$(O_DIR)/%)
ESHISRC := $(shell cat eshi.list)
TARGETS = $(TESTSRC:%.c=$(O_DIR)/%) $(ESHISRC:%.c=$(O_DIR)/%.eshi)
NEEDLIBM = $(FPSRC:%.c=$(O_DIR)/%)
DEPFILES = $(ALLSRC:%.c=$(O_DIR)/%.d)
@ -16,22 +17,32 @@ TEST_CPPFLAGS := $(BASE_CPPFLAGS) \
-I. \
-I../include \
-I$(O_DIR)/../include
TEST_CFLAGS := $(TEST_CPPFLAGS) $(BASE_CFLAGS)
override CFLAGS := $(TEST_CFLAGS) $(CFLAGS)
TEST_LDFLAGS := $(O_DIR)/../lib/libevl.so.$(EVL_IVERSION) -lpthread -lrt
override LDFLAGS := $(TEST_LDFLAGS) $(LDFLAGS)
$(NEEDLIBM): override LDFLAGS += -lm
TEST_LDFLAGS := $(O_DIR)/../lib/libevl.so.$(EVL_IVERSION) -lpthread -lrt $(LDFLAGS)
$(NEEDLIBM): override TEST_LDFLAGS += -lm
ESHI_LDFLAGS := $(O_DIR)/../eshi/libeshi.so.$(EVL_IVERSION) -lpthread -lrt $(LDFLAGS)
ESHI_CPPFLAGS := $(BASE_CPPFLAGS) \
-D__ESHI__ \
-I. \
-I../include/eshi \
-I../include \
-I$(O_DIR)/../include
ESHI_CFLAGS := $(ESHI_CPPFLAGS) $(BASE_CFLAGS)
all: output-Makefile $(TARGETS)
$(TARGETS): $(HELPOBJ)
install: all
$(Q)for bin in $(TESTSRC:%.c=%); do \
$(call inst-cmd,tests,$(Q)for bin in $(TESTSRC:%.c=%); do \
$(INSTALL) -D $(O_DIR)/$$bin $(DESTDIR)/$(testdir)/$$bin; \
done
done)
$(call inst-cmd,tests-eshi,$(Q)for bin in $(ESHISRC:%.c=%.eshi); do \
$(INSTALL) -D $(O_DIR)/$$bin $(DESTDIR)/$(testdir)/eshi/$$bin; \
done)
clean clobber mrproper:
$(Q)$(RM) -f $(TARGETS) $(DEPFILES) $(HELPOBJ)
@ -40,6 +51,9 @@ $(O_DIR)/%.o: %.c
$(call cc-cmd,$@,$(Q)$(CC) -o $(@) $< -c $(CFLAGS))
$(O_DIR)/%: %.c
$(call ccld-cmd,$@,$(Q)$(CC) -o $(@) $< $(HELPOBJ) $(CFLAGS) $(LDFLAGS))
$(call ccld-cmd,$@,$(Q)$(CC) -o $(@) $< $(HELPOBJ) $(TEST_CFLAGS) $(TEST_LDFLAGS))
$(O_DIR)/%.eshi: %.c
$(call ccld-cmd,$@,$(Q)$(CC) -o $(@) $< $(HELPOBJ) $(ESHI_CFLAGS) $(ESHI_LDFLAGS))
-include $(DEPFILES)

11
tests/eshi.list Normal file
View File

@ -0,0 +1,11 @@
clock-timer-periodic.c
detach-self.c
heap-torture.c
monitor-event.c
monitor-flags.c
poll-nested.c
poll-sem.c
proxy-eventfd.c
proxy-pipe.c
sem-timedwait.c
sem-wait.c

View File

@ -136,7 +136,11 @@ int main(int argc, char *argv[])
__Tcall_assert(ret, evl_lock_mutex(&c.lock));
c.condition = 3;
#ifdef __ESHI__
__Tcall_assert(ret, evl_signal_event(&c.event));
#else
__Tcall_assert(ret, evl_signal_thread(&c.event, receiverfd));
#endif
__Tcall_assert(ret, evl_unlock_mutex(&c.lock));
__Texpr_assert(pthread_join(receiver, &status) == 0);

View File

@ -8,6 +8,7 @@
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <evl/compiler.h>
#include <evl/thread.h>
#include <evl/flags.h>
#include <evl/sem.h>
@ -50,12 +51,14 @@ static void *flags_receiver(void *arg)
if (!__Texpr(bits == 0x12121212))
goto fail;
#ifndef __ESHI__
/* Flag group should be cleared. */
if (!__Tcall(ret, evl_peek_flags(&p->flags, &bits)))
goto fail;
if (!__Texpr(bits == 0))
goto fail;
#endif
/* Trywait should fail with -EAGAIN. */
if (!__Fcall(ret, evl_trywait_flags(&p->flags, &bits)))
@ -82,7 +85,7 @@ fail:
int main(int argc, char *argv[])
{
int tfd, ffd, sfd, ret, bits;
int tfd, ffd, sfd, ret, bits __maybe_unused;
struct sched_param param;
struct test_context c;
void *status = NULL;
@ -111,8 +114,10 @@ int main(int argc, char *argv[])
__Tcall_assert(ret, evl_put_sem(&c.start));
__Tcall_assert(ret, evl_get_sem(&c.sem));
__Tcall_assert(ret, evl_post_flags(&c.flags, 0x12121212));
#ifndef __ESHI__
__Tcall_assert(ret, evl_peek_flags(&c.flags, &bits));
__Texpr_assert(bits == 0x12121212);
#endif
__Tcall_assert(ret, evl_get_sem(&c.sem));
__Tcall_assert(ret, evl_udelay(1000));
__Tcall_assert(ret, evl_post_flags(&c.flags, 0x76767676));