diff --git a/Makefile b/Makefile index b73955b..9f7ae1b 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/eshi/Makefile b/eshi/Makefile new file mode 100644 index 0000000..019db3a --- /dev/null +++ b/eshi/Makefile @@ -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) diff --git a/eshi/README.md b/eshi/README.md new file mode 100644 index 0000000..59d4c7e --- /dev/null +++ b/eshi/README.md @@ -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. diff --git a/eshi/clock.c b/eshi/clock.c new file mode 100644 index 0000000..52abb50 --- /dev/null +++ b/eshi/clock.c @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include + +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; +} diff --git a/eshi/event.c b/eshi/event.c new file mode 100644 index 0000000..2643a35 --- /dev/null +++ b/eshi/event.c @@ -0,0 +1,130 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#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); +} diff --git a/eshi/flags.c b/eshi/flags.c new file mode 100644 index 0000000..abf5019 --- /dev/null +++ b/eshi/flags.c @@ -0,0 +1,189 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/eshi/heap.c b/eshi/heap.c new file mode 100644 index 0000000..0bf2fc5 --- /dev/null +++ b/eshi/heap.c @@ -0,0 +1,7 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include "../lib/heap.c" diff --git a/eshi/init.c b/eshi/init.c new file mode 100644 index 0000000..87a42e8 --- /dev/null +++ b/eshi/init.c @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#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; +} diff --git a/eshi/internal.h b/eshi/internal.h new file mode 100644 index 0000000..66b259f --- /dev/null +++ b/eshi/internal.h @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_INTERNAL_H +#define _EVL_ESHI_INTERNAL_H + +#include +#include +#include +#include + +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 */ diff --git a/eshi/mutex.c b/eshi/mutex.c new file mode 100644 index 0000000..73d1d18 --- /dev/null +++ b/eshi/mutex.c @@ -0,0 +1,197 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#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); +} diff --git a/eshi/poll.c b/eshi/poll.c new file mode 100644 index 0000000..1538be5 --- /dev/null +++ b/eshi/poll.c @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#include +#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); +} diff --git a/eshi/proxy.c b/eshi/proxy.c new file mode 100644 index 0000000..00290f2 --- /dev/null +++ b/eshi/proxy.c @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/eshi/sched.c b/eshi/sched.c new file mode 100644 index 0000000..8d3c39f --- /dev/null +++ b/eshi/sched.c @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#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, ¶m); +} + +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, ¶m); + if (ret) + return -ret; + + attrs->sched_policy = policy; + attrs->sched_priority = param.sched_priority; + + return 0; +} diff --git a/eshi/sem.c b/eshi/sem.c new file mode 100644 index 0000000..9849574 --- /dev/null +++ b/eshi/sem.c @@ -0,0 +1,184 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/eshi/thread.c b/eshi/thread.c new file mode 100644 index 0000000..66be9d1 --- /dev/null +++ b/eshi/thread.c @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/eshi/timer.c b/eshi/timer.c new file mode 100644 index 0000000..9fcc3a7 --- /dev/null +++ b/eshi/timer.c @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#include +#include +#include +#include +#include + +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; +} diff --git a/include/Makefile b/include/Makefile index 79a34ae..d8bc90d 100644 --- a/include/Makefile +++ b/include/Makefile @@ -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) diff --git a/include/eshi/evl/clock.h b/include/eshi/evl/clock.h new file mode 100644 index 0000000..99ff73b --- /dev/null +++ b/include/eshi/evl/clock.h @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_CLOCK_H +#define _EVL_ESHI_CLOCK_H + +#include +#include +#include + +#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 */ diff --git a/include/eshi/evl/event.h b/include/eshi/evl/event.h new file mode 100644 index 0000000..bd53f75 --- /dev/null +++ b/include/eshi/evl/event.h @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_EVENT_H +#define _EVL_ESHI_EVENT_H + +#include +#include +#include +#include + +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 */ diff --git a/include/eshi/evl/evl.h b/include/eshi/evl/evl.h new file mode 100644 index 0000000..6682115 --- /dev/null +++ b/include/eshi/evl/evl.h @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_EVL_H +#define _EVL_ESHI_EVL_H + +#include + +#define __EVL__ 5 /* API revision */ + +#ifdef __cplusplus +extern "C" { +#endif + +int evl_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _EVL_ESHI_EVL_H */ diff --git a/include/eshi/evl/flags.h b/include/eshi/evl/flags.h new file mode 100644 index 0000000..5d85fc0 --- /dev/null +++ b/include/eshi/evl/flags.h @@ -0,0 +1,69 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_FLAGS_H +#define _EVL_ESHI_FLAGS_H + +#include +#include + +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 */ diff --git a/include/eshi/evl/mutex.h b/include/eshi/evl/mutex.h new file mode 100644 index 0000000..fe63eac --- /dev/null +++ b/include/eshi/evl/mutex.h @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_MUTEX_H +#define _EVL_ESHI_MUTEX_H + +#include +#include +#include + +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 */ diff --git a/include/eshi/evl/poll.h b/include/eshi/evl/poll.h new file mode 100644 index 0000000..81b683a --- /dev/null +++ b/include/eshi/evl/poll.h @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_POLL_H +#define _EVL_ESHI_POLL_H + +#include +#include +#include + +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 */ diff --git a/include/eshi/evl/proxy.h b/include/eshi/evl/proxy.h new file mode 100644 index 0000000..cc8932b --- /dev/null +++ b/include/eshi/evl/proxy.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_PROXY_H +#define _EVL_ESHI_PROXY_H + +#include +#include +#include +#include +#include + +#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 */ diff --git a/include/eshi/evl/sched.h b/include/eshi/evl/sched.h new file mode 100644 index 0000000..3ae8724 --- /dev/null +++ b/include/eshi/evl/sched.h @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#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 */ diff --git a/include/eshi/evl/sem.h b/include/eshi/evl/sem.h new file mode 100644 index 0000000..4587cfb --- /dev/null +++ b/include/eshi/evl/sem.h @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_SEM_H +#define _EVL_ESHI_SEM_H + +#include +#include + +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 */ diff --git a/include/eshi/evl/syscall.h b/include/eshi/evl/syscall.h new file mode 100644 index 0000000..febf92e --- /dev/null +++ b/include/eshi/evl/syscall.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_SYSCALL_H +#define _EVL_ESHI_SYSCALL_H + +#include +#include +#include + +#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 */ diff --git a/include/eshi/evl/thread.h b/include/eshi/evl/thread.h new file mode 100644 index 0000000..402436d --- /dev/null +++ b/include/eshi/evl/thread.h @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_THREAD_H +#define _EVL_ESHI_THREAD_H + +#include +#include +#include +#include + +/* 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 */ diff --git a/include/eshi/evl/timer.h b/include/eshi/evl/timer.h new file mode 100644 index 0000000..3db2f9e --- /dev/null +++ b/include/eshi/evl/timer.h @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#ifndef _EVL_ESHI_TIMER_H +#define _EVL_ESHI_TIMER_H + +#include +#include + +#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 */ diff --git a/include/eshi/evl/uapi.h b/include/eshi/evl/uapi.h new file mode 100644 index 0000000..ee4080a --- /dev/null +++ b/include/eshi/evl/uapi.h @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 Philippe Gerum + */ + +#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 */ diff --git a/tests/Makefile b/tests/Makefile index 1719122..b225dd7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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) diff --git a/tests/eshi.list b/tests/eshi.list new file mode 100644 index 0000000..cf333f4 --- /dev/null +++ b/tests/eshi.list @@ -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 diff --git a/tests/monitor-event.c b/tests/monitor-event.c index 5233016..26cdcb3 100644 --- a/tests/monitor-event.c +++ b/tests/monitor-event.c @@ -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); diff --git a/tests/monitor-flags.c b/tests/monitor-flags.c index dab77fc..4437988 100644 --- a/tests/monitor-flags.c +++ b/tests/monitor-flags.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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));