Commit f27fc993 authored by Philippe Gerum's avatar Philippe Gerum
Browse files

lib: introduce the interface to observables



Since ABI 23, the core provides the new observable element, which
enables the observer design pattern. Any EVL thread is in and of
itself an observable which can be monitored for events too.

As a by-product, the poll interface can now be given a user-defined
opaque data when subscribing file descriptors to poll elements, which
the core passes back on return to evl_poll().
Signed-off-by: default avatarPhilippe Gerum <rpm@xenomai.org>
parent 197b2d4d
......@@ -38,6 +38,7 @@ evl_open_mutex
evl_open_event
evl_open_flags
evl_open_sem
The observable API.
== VARIATION(S)
......
......@@ -16,7 +16,8 @@ int evl_new_poll(void)
return epoll_create1(EPOLL_CLOEXEC);
}
int evl_add_pollfd(int efd, int newfd, unsigned int events)
int evl_add_pollfd(int efd, int newfd, unsigned int events,
union evl_value pollval)
{
struct epoll_event ev;
int ret;
......@@ -45,7 +46,8 @@ int evl_del_pollfd(int efd, int delfd)
return 0;
}
int evl_mod_pollfd(int efd, int modfd, unsigned int events)
int evl_mod_pollfd(int efd, int modfd, unsigned int events,
union evl_value pollval)
{
struct epoll_event ev;
int ret;
......
......@@ -14,6 +14,7 @@
struct evl_poll_event {
__u32 fd;
__u32 events;
union evl_value pollval;
};
#ifdef __cplusplus
......@@ -23,12 +24,14 @@ extern "C" {
int evl_new_poll(void);
int evl_add_pollfd(int efd, int newfd,
unsigned int events);
unsigned int events,
union evl_value pollval);
int evl_del_pollfd(int efd, int delfd);
int evl_mod_pollfd(int efd, int modfd,
unsigned int events);
unsigned int events,
union evl_value pollval);
int evl_timedpoll(int efd, struct evl_poll_event *pollset,
int nrset, struct timespec *timeout);
......
......@@ -7,6 +7,8 @@
#ifndef _EVL_ESHI_UAPI_H
#define _EVL_ESHI_UAPI_H
#include <stdint.h>
#define EVL_CLOCK_MONOTONIC_DEV "monotonic"
#define EVL_CLOCK_REALTIME_DEV "realtime"
#define EVL_CLOCK_DEV "clock"
......@@ -15,5 +17,14 @@
#define EVL_PROXY_DEV "proxy"
#define EVL_THREAD_DEV "thread"
#define EVL_XBUF_DEV "xbuf"
#define EVL_OBSERVABLE_DEV "observable"
union evl_value {
int32_t val;
int64_t lval;
void *ptr;
};
#define evl_nil ((union evl_value){ .lval = 0 })
#endif /* _EVL_ESHI_UAPI_H */
......@@ -20,9 +20,9 @@
#include <evl/poll.h>
#include <evl/proxy.h>
#define __EVL__ 15 /* API version */
#define __EVL__ 16 /* API version */
#define EVL_ABI_PREREQ 21
#define EVL_ABI_PREREQ 23
struct evl_version {
int api_level; /* libevl.so: __EVL__ */
......
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2020 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_OBSERVABLE_H
#define _EVL_OBSERVABLE_H
#include <time.h>
#include <stdint.h>
#include <evl/syscall.h>
#include <uapi/evl/observable.h>
#include <uapi/evl/factory.h>
struct evl_notification {
uint32_t tag;
uint32_t serial;
int32_t issuer;
union evl_value event;
struct timespec date;
};
#define evl_new_observable(__fmt, __args...) \
evl_create_observable(EVL_CLONE_PRIVATE, __fmt, ##__args)
#ifdef __cplusplus
extern "C" {
#endif
int evl_create_observable(int flags, const char *fmt, ...);
int evl_update_observable(int ofd, const struct evl_notice *ntc,
int nr);
int evl_read_observable(int ofd, struct evl_notification *nf,
int nr);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_OBSERVABLE_H */
......@@ -19,12 +19,14 @@ extern "C" {
int evl_new_poll(void);
int evl_add_pollfd(int efd, int newfd,
unsigned int events);
unsigned int events,
union evl_value pollval);
int evl_del_pollfd(int efd, int delfd);
int evl_mod_pollfd(int efd, int modfd,
unsigned int events);
unsigned int events,
union evl_value pollval);
int evl_timedpoll(int efd, struct evl_poll_event *pollset,
int nrset, const struct timespec *timeout);
......
......@@ -76,6 +76,12 @@ int evl_set_thread_mode(int efd, int mask,
int evl_clear_thread_mode(int efd, int mask,
int *oldmask);
int evl_subscribe(int ofd,
unsigned int backlog_count,
int flags);
int evl_unsubscribe(int ofd);
#ifdef __cplusplus
}
#endif
......
......@@ -48,8 +48,7 @@ static int init_event_vargs(struct evl_event *evt,
attrs.protocol = EVL_EVENT_GATED;
attrs.clockfd = clockfd;
attrs.initval = 0;
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs,
flags & EVL_CLONE_MASK, &eids);
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs, flags, &eids);
free(name);
if (efd < 0)
return efd;
......
......@@ -49,8 +49,7 @@ int evl_create_flags(struct evl_flags *flg, int clockfd,
attrs.protocol = EVL_EVENT_MASK;
attrs.clockfd = clockfd;
attrs.initval = initval;
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs,
flags & EVL_CLONE_MASK, &eids);
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs, flags, &eids);
free(name);
if (efd < 0)
return efd;
......
......@@ -28,6 +28,21 @@ static void lart_once(void)
pthread_once(&lart_once, do_lart_once);
}
static int flip_fd_flags(int efd, int cmd, int flags)
{
int ret;
ret = fcntl(efd, cmd == F_SETFD ? F_GETFD : F_GETFL, 0);
if (ret < 0)
return -errno;
ret = fcntl(efd, cmd, ret | flags);
if (ret)
return -errno;
return 0;
}
/*
* Creating an EVL element is done in the following steps:
*
......@@ -51,6 +66,12 @@ int create_evl_element(const char *type, const char *name,
char *fdevname, *edevname = NULL;
struct evl_clone_req clone;
int ffd, efd, ret;
bool nonblock;
nonblock = !!(clone_flags & EVL_CLONE_NONBLOCK);
/* Strip off user-only bits. */
clone_flags &= EVL_CLONE_MASK;
clone_flags &= ~EVL_CLONE_NONBLOCK;
ret = asprintf(&fdevname, "/dev/evl/%s/clone", type);
if (ret < 0)
......@@ -101,16 +122,14 @@ int create_evl_element(const char *type, const char *name,
efd = clone.efd;
}
ret = fcntl(efd, F_GETFD, 0);
if (ret < 0) {
ret = -errno;
ret = flip_fd_flags(efd, F_SETFD, O_CLOEXEC);
if (ret)
goto out_element;
}
ret = fcntl(efd, F_SETFD, ret | O_CLOEXEC);
if (ret) {
ret = -errno;
goto out_element;
if (nonblock) {
ret = flip_fd_flags(efd, F_SETFL, O_NONBLOCK);
if (ret)
goto out_element;
}
if (eids)
......
......@@ -62,8 +62,7 @@ static int init_mutex_vargs(struct evl_mutex *mutex,
attrs.protocol = protocol;
attrs.clockfd = clockfd;
attrs.initval = ceiling;
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs,
flags & EVL_CLONE_MASK, &eids);
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs, flags, &eids);
free(name);
if (efd < 0)
return efd;
......
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2020 Philippe Gerum <rpm@xenomai.org>
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <evl/compiler.h>
#include <evl/thread.h>
#include <evl/observable.h>
#include <evl/syscall.h>
#include "internal.h"
int evl_create_observable(int flags, const char *fmt, ...)
{
int ret, efd;
va_list ap;
char *name;
va_start(ap, fmt);
ret = vasprintf(&name, fmt, ap);
va_end(ap);
if (ret < 0)
return -ENOMEM;
efd = create_evl_element(EVL_OBSERVABLE_DEV, name, NULL, flags, NULL);
free(name);
return efd;
}
static bool wants_oob_io(void)
{
/*
* Only non-EVL threads or members of the SCHED_WEAK class
* should call in-band.
*/
if (evl_current == EVL_NO_HANDLE)
return false;
return !(evl_get_current_mode() & T_WEAK);
}
int evl_update_observable(int ofd, const struct evl_notice *ntc, int nr)
{
ssize_t ret;
if (!wants_oob_io())
ret = write(ofd, ntc, nr * sizeof(*ntc));
else
ret = oob_write(ofd, ntc, nr * sizeof(*ntc));
if (ret < 0)
return errno == EAGAIN ? 0 : -errno;
return ret / sizeof(*ntc);
}
static ssize_t do_read(int ofd, struct evl_notification *nf, int nr,
ssize_t (*readfn)(int ofd, void *buf, size_t count))
{
struct __evl_notification _nf __maybe_unused;
ssize_t ret, _ret __maybe_unused;
/*
* This mess is exclusively intended not to expose the
* __evl_timespec type embedded into the __evl_notification
* descriptor to users. Legacy 32bit systems with
* Y0238-unsafe C libraries have to pay a price for this, by
* reading every notification one after another instead of
* pulling a bulk - this stupidly trivial way seems acceptable
* for those platforms. For all others, struct __evl_timespec
* used in kernel space and timespec in userland have the same
* memory layout, so we may read the notifications in one gulp
* directly into the user buffer.
*/
#if __WORDSIZE == 64 || __TIMESIZE == 64
ret = readfn(ofd, nf, nr * sizeof(*nf));
#else
ret = 0;
while (nr-- > 0) {
_ret = readfn(ofd, &_nf, sizeof(_nf));
if (_ret <= 0)
return ret ?: _ret;
nf->tag = _nf.tag;
nf->serial = _nf.serial;
nf->issuer = _nf.issuer;
nf->event = _nf.event;
nf->date.tv_sec = (long)_nf.date.tv_sec;
nf->date.tv_nsec = _nf.date.tv_nsec;
ret += sizeof(*nf);
nf++;
}
#endif
return ret;
}
int evl_read_observable(int ofd, struct evl_notification *nf, int nr)
{
ssize_t ret;
if (!wants_oob_io())
ret = do_read(ofd, nf, nr, read);
else
ret = do_read(ofd, nf, nr, oob_read);
if (ret < 0)
return -errno;
return ret / sizeof(*nf);
}
......@@ -18,7 +18,8 @@ int evl_new_poll(void)
return create_evl_file(EVL_POLL_DEV);
}
static int update_pollset(int efd, int op, int fd, unsigned int events)
static int update_pollset(int efd, int op, int fd, unsigned int events,
union evl_value pollval)
{
struct evl_poll_ctlreq creq;
int ret;
......@@ -26,24 +27,27 @@ static int update_pollset(int efd, int op, int fd, unsigned int events)
creq.action = op;
creq.fd = fd;
creq.events = events;
creq.pollval = pollval;
ret = oob_ioctl(efd, EVL_POLIOC_CTL, &creq);
return ret ? -errno : 0;
}
int evl_add_pollfd(int efd, int fd, unsigned int events)
int evl_add_pollfd(int efd, int fd, unsigned int events,
union evl_value pollval)
{
return update_pollset(efd, EVL_POLL_CTLADD, fd, events);
return update_pollset(efd, EVL_POLL_CTLADD, fd, events, pollval);
}
int evl_del_pollfd(int efd, int fd)
{
return update_pollset(efd, EVL_POLL_CTLDEL, fd, 0);
return update_pollset(efd, EVL_POLL_CTLDEL, fd, 0, evl_nil);
}
int evl_mod_pollfd(int efd, int fd, unsigned int events)
int evl_mod_pollfd(int efd, int fd,
unsigned int events, union evl_value pollval)
{
return update_pollset(efd, EVL_POLL_CTLMOD, fd, events);
return update_pollset(efd, EVL_POLL_CTLMOD, fd, events, pollval);
}
static int do_poll(int efd, struct evl_poll_event *pollset,
......
......@@ -49,8 +49,7 @@ int evl_create_proxy(int targetfd, size_t bufsz, size_t granularity,
attrs.fd = targetfd;
attrs.bufsz = bufsz;
attrs.granularity = granularity;
efd = create_evl_element(EVL_PROXY_DEV, name, &attrs,
flags & EVL_CLONE_MASK, NULL);
efd = create_evl_element(EVL_PROXY_DEV, name, &attrs, flags, NULL);
free(name);
return efd;
......
......@@ -48,8 +48,7 @@ int evl_create_sem(struct evl_sem *sem, int clockfd,
attrs.protocol = EVL_EVENT_COUNT;
attrs.clockfd = clockfd;
attrs.initval = initval;
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs,
flags & EVL_CLONE_MASK, &eids);
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs, flags, &eids);
free(name);
if (efd < 0)
return efd;
......
......@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <uapi/evl/factory.h>
#include <uapi/evl/control.h>
#include <uapi/evl/observable.h>
#include "internal.h"
__thread __attribute__ ((tls_model (EVL_TLS_MODEL)))
......@@ -81,8 +82,7 @@ int evl_attach_thread(int flags, const char *fmt, ...)
if (ret < 0)
return -ENOMEM;
efd = create_evl_element(EVL_THREAD_DEV, name, NULL,
flags & EVL_CLONE_MASK, &eids);
efd = create_evl_element(EVL_THREAD_DEV, name, NULL, flags, &eids);
free(name);
if (efd < 0)
return efd;
......@@ -234,3 +234,28 @@ int evl_clear_thread_mode(int efd, int mask, int *oldmask)
{
return do_thread_mode(efd, EVL_THRIOC_CLEAR_MODE, mask, oldmask);
}
int evl_subscribe(int efd, unsigned int backlog_count, int flags)
{
struct evl_subscription sub;
int ret;
sub.backlog_count = backlog_count;
sub.flags = flags;
ret = ioctl(efd, EVL_OBSIOC_SUBSCRIBE, &sub);
if (ret && errno == ENOTTY)
return -EPERM;
return ret ? -errno : 0;
}
int evl_unsubscribe(int efd)
{
int ret;
ret = ioctl(efd, EVL_OBSIOC_UNSUBSCRIBE);
if (ret && errno == ENOTTY)
return -EPERM;
return ret ? -errno : 0;
}
......@@ -26,8 +26,7 @@ int evl_create_xbuf(size_t i_bufsz, size_t o_bufsz,
attrs.i_bufsz = i_bufsz;
attrs.o_bufsz = o_bufsz;
efd = create_evl_element(EVL_XBUF_DEV, name, &attrs,
flags & EVL_CLONE_MASK, NULL);
efd = create_evl_element(EVL_XBUF_DEV, name, &attrs, flags, NULL);
free(name);
return efd;
......
/*
* SPDX-License-Identifier: MIT
*
* COMPILE-TESTING ONLY.
*/
#include <evl/observable.h>
int main(int argc, char *argv[])
{
evl_new_observable("test");
evl_create_observable(EVL_CLONE_PRIVATE, "test");
return 0;
}
......@@ -14,9 +14,9 @@ int main(int argc, char *argv[])
int efd;
efd = evl_new_poll();
evl_add_pollfd(efd, 1, POLLIN);
evl_add_pollfd(efd, 1, POLLIN, evl_nil);
evl_del_pollfd(efd, 1);
evl_mod_pollfd(efd, 1, POLLOUT);
evl_mod_pollfd(efd, 1, POLLOUT, evl_nil);
evl_read_clock(EVL_CLOCK_MONOTONIC, &timeout);
evl_poll(efd, &pollset, 1);
evl_timedpoll(efd, &pollset, 1, &timeout);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment