Commit 8dffaaad authored by Philippe Gerum's avatar Philippe Gerum
Browse files

lib: add support for event flag groups

Event flags are boolean conditions represented by a single bit,
grouped in 32bit words. All bits from a group are initially set to
zero.

An event flag group is a lightweight notification mechanism. The
application can send bitmasks to raise individual bits from the group
(i.e. group_value |= bits), or wait for the group to have at least one
bit set for satisfying the request. In the latter case, the group
value is read then cleared atomically, and the collected bits are
returned to the thread heading the wait queue.
parent 68dd8674
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#ifndef _EVL_FLAGS_H
#define _EVL_FLAGS_H
#include <time.h>
#include <evl/atomic.h>
#include <linux/types.h>
#include <uapi/evl/types.h>
#include <uapi/evl/monitor.h>
struct evl_thread;
struct evl_flags {
unsigned int magic;
union {
struct {
fundle_t fundle;
struct evl_monitor_state *state;
int efd;
} 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);
int evl_peek_flags(struct evl_flags *flg,
int *r_bits);
#ifdef __cplusplus
}
#endif
#endif /* _EVL_FLAGS_H */
......@@ -210,6 +210,7 @@ int evl_timedwait(struct evl_condvar *cv,
req.gatefd = mutex->active.efd;
req.timeout = *timeout;
req.status = -EINVAL;
req.value = 0; /* dummy */
unwait.ureq.gatefd = req.gatefd;
unwait.efd = cv->active.efd;
......
/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
*/
#include <stdarg.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <evl/atomic.h>
#include <evl/evl.h>
#include <evl/flags.h>
#include <evl/thread.h>
#include <evl/syscall.h>
#include <linux/types.h>
#include <uapi/evl/factory.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, ...)
{
struct evl_monitor_attrs attrs;
struct evl_element_ids eids;
int efd, ret;
va_list ap;
char *name;
if (evl_shared_memory == NULL)
return -ENXIO;
va_start(ap, fmt);
ret = vasprintf(&name, fmt, ap);
va_end(ap);
if (ret < 0)
return -ENOMEM;
attrs.type = EVL_MONITOR_EVENT;
attrs.protocol = EVL_EVENT_MASK;
attrs.clockfd = clockfd;
attrs.initval = initval;
efd = create_evl_element(EVL_MONITOR_DEV, name, &attrs, &eids);
free(name);
if (efd < 0)
return efd;
flg->active.state = evl_shared_memory + eids.state_offset;
flg->active.fundle = eids.fundle;
flg->active.efd = efd;
flg->magic = __FLAGS_ACTIVE_MAGIC;
return 0;
}
int evl_open_flags(struct evl_flags *flg, const char *fmt, ...)
{
struct evl_monitor_binding bind;
int ret, efd;
va_list ap;
va_start(ap, fmt);
efd = open_evl_element_vargs(EVL_MONITOR_DEV, fmt, ap);
va_end(ap);
if (efd < 0)
return efd;
ret = ioctl(efd, EVL_MONIOC_BIND, &bind);
if (ret) {
ret = -errno;
goto fail;
}
if (bind.type != EVL_MONITOR_EVENT ||
bind.protocol != EVL_EVENT_MASK) {
ret = -EINVAL;
goto fail;
}
flg->active.state = evl_shared_memory + bind.eids.state_offset;
flg->active.fundle = bind.eids.fundle;
flg->active.efd = efd;
flg->magic = __FLAGS_ACTIVE_MAGIC;
return 0;
fail:
close(efd);
return ret;
}
int evl_close_flags(struct evl_flags *flg)
{
int ret;
if (flg->magic == __FLAGS_UNINIT_MAGIC)
return 0;
if (flg->magic != __FLAGS_ACTIVE_MAGIC)
return -EINVAL;
ret = close(flg->active.efd);
if (ret)
return -errno;
flg->active.fundle = EVL_NO_HANDLE;
flg->active.state = NULL;
flg->magic = __FLAGS_DEAD_MAGIC;
return 0;
}
static int check_sanity(struct evl_flags *flg)
{
if (flg->magic == __FLAGS_UNINIT_MAGIC)
return evl_new_flags(flg,
flg->uninit.clockfd,
flg->uninit.initval,
flg->uninit.name);
return flg->magic != __FLAGS_ACTIVE_MAGIC ? -EINVAL : 0;
}
static int try_wait(struct evl_monitor_state *state)
{
int val, prev;
val = atomic_read(&state->u.event.value);
if (!val)
return 0;
do {
prev = val;
val = atomic_cmpxchg(&state->u.event.value, prev, 0);
if (!val)
return 0;
} while (val != prev);
return val;
}
int evl_timedwait_flags(struct evl_flags *flg,
const struct timespec *timeout, int *r_bits)
{
struct evl_monitor_state *state;
struct evl_monitor_waitreq req;
int mode, ret, cancel_type;
fundle_t current;
current = evl_get_current();
if (current == EVL_NO_HANDLE)
return -EPERM;
ret = check_sanity(flg);
if (ret)
return ret;
/*
* Threads running in-band and/or enabling some debug features
* must go through the slow syscall path.
*/
state = flg->active.state;
mode = evl_get_current_mode();
if (!(mode & (T_INBAND|T_WEAK|T_DEBUG))) {
ret = try_wait(state);
if (ret) {
*r_bits = ret;
return 0;
}
}
req.gatefd = -1;
req.timeout = *timeout;
req.status = -EINVAL;
req.value = 0;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &cancel_type);
ret = oob_ioctl(flg->active.efd, EVL_MONIOC_WAIT, &req);
pthread_setcanceltype(cancel_type, NULL);
if (ret)
return -errno;
if (req.status)
return req.status;
*r_bits = req.value;
return 0;
}
int evl_wait_flags(struct evl_flags *flg, int *r_bits)
{
struct timespec timeout = { .tv_sec = 0, .tv_nsec = 0 };
return evl_timedwait_flags(flg, &timeout, r_bits);
}
int evl_trywait_flags(struct evl_flags *flg, int *r_bits)
{
int ret;
ret = check_sanity(flg);
if (ret)
return ret;
ret = try_wait(flg->active.state);
if (!ret)
return -EAGAIN;
*r_bits = ret;
return 0;
}
int evl_post_flags(struct evl_flags *flg, int bits)
{
struct evl_monitor_state *state;
int val, prev, next, ret;
__s32 mask = bits;
ret = check_sanity(flg);
if (ret)
return ret;
if (!bits)
return -EINVAL;
state = flg->active.state;
val = atomic_read(&state->u.event.value);
if (!val) {
slow_path:
if (evl_get_current())
ret = oob_ioctl(flg->active.efd, EVL_MONIOC_SIGNAL, &mask);
else
ret = ioctl(flg->active.efd, EVL_MONIOC_SIGNAL, &mask);
return ret ? -errno : 0;
}
do {
prev = val;
next = prev | bits;
val = atomic_cmpxchg(&state->u.event.value, prev, next);
/* Check if somebody sneaked in the wait queue. */
if (!val)
goto slow_path;
} while (val != prev);
return 0;
}
int evl_peek_flags(struct evl_flags *flg, int *r_bits)
{
if (flg->magic != __FLAGS_ACTIVE_MAGIC)
return -EINVAL;
*r_bits = atomic_read(&flg->active.state->u.event.value);
return 0;
}
......@@ -190,6 +190,7 @@ int evl_timedget(struct evl_sem *sem, const struct timespec *timeout)
req.gatefd = -1;
req.timeout = *timeout;
req.status = -EINVAL;
req.value = 0; /* dummy */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &cancel_type);
ret = oob_ioctl(sem->active.efd, EVL_MONIOC_WAIT, &req);
......@@ -230,10 +231,10 @@ int evl_put(struct evl_sem *sem)
if (val < 0) {
slow_path:
if (evl_get_current())
ret = oob_ioctl(sem->active.efd, EVL_MONIOC_SIGNAL);
ret = oob_ioctl(sem->active.efd, EVL_MONIOC_SIGNAL, NULL);
else
/* In-band threads may post pended sema4s. */
ret = ioctl(sem->active.efd, EVL_MONIOC_SIGNAL);
ret = ioctl(sem->active.efd, EVL_MONIOC_SIGNAL, NULL);
return ret ? -errno : 0;
}
......
/*
* SPDX-License-Identifier: MIT
*/
#include <sys/types.h>
#include <time.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <evl/thread.h>
#include <evl/flags.h>
#include <evl/sem.h>
#include <evl/clock.h>
#include "helpers.h"
#define LOW_PRIO 1
#define HIGH_PRIO 2
struct test_context {
struct evl_flags flags;
struct evl_sem start;
struct evl_sem sem;
};
static void *flags_receiver(void *arg)
{
struct test_context *p = arg;
struct timespec now, timeout;
int ret, tfd, bits;
__Tcall_assert(tfd, evl_attach_self("monitor-flags-receiver:%d", getpid()));
__Tcall_assert(ret, evl_get(&p->start));
evl_read_clock(EVL_CLOCK_MONOTONIC, &now);
timespec_add_ns(&timeout, &now, 200000000); /* 200ms */
/* Sender is quiet: expect timeout. */
if (!__Fcall(ret, evl_timedwait_flags(&p->flags, &timeout, &bits)) ||
!__Texpr(ret == -ETIMEDOUT))
goto fail;
__Tcall_assert(ret, evl_put(&p->sem));
/* Sender should have sent 0x12121212. */
if (!__Tcall(ret, evl_timedwait_flags(&p->flags, &timeout, &bits)))
goto fail;
if (!__Texpr(bits == 0x12121212))
goto fail;
/* Flag group should be cleared. */
if (!__Tcall(ret, evl_peek_flags(&p->flags, &bits)))
goto fail;
if (!__Texpr(bits == 0))
goto fail;
/* Trywait should fail with -EAGAIN. */
if (!__Fcall(ret, evl_trywait_flags(&p->flags, &bits)))
goto fail;
if (!__Texpr(ret == -EAGAIN))
goto fail;
__Tcall_assert(ret, evl_put(&p->sem));
/* Sender should send 0x76767676 in a moment. */
if (!__Tcall(ret, evl_wait_flags(&p->flags, &bits)))
goto fail;
if (!__Texpr(bits == 0x76767676))
goto fail;
__Tcall_assert(ret, evl_put(&p->sem));
return NULL;
fail:
exit(1);
}
int main(int argc, char *argv[])
{
int tfd, ffd, sfd, ret, bits;
struct sched_param param;
struct test_context c;
void *status = NULL;
pthread_t receiver;
char *name;
param.sched_priority = HIGH_PRIO;
__Texpr_assert(pthread_setschedparam(pthread_self(),
SCHED_FIFO, &param) == 0);
/* EVL inherits the inband scheduling params upon attachment. */
__Tcall_assert(tfd, evl_attach_self("monitor-flags:%d", getpid()));
name = get_unique_name(EVL_MONITOR_DEV, 0);
__Tcall_assert(sfd, evl_new_sem(&c.sem, EVL_CLOCK_MONOTONIC, 0, name));
name = get_unique_name(EVL_MONITOR_DEV, 1);
__Tcall_assert(sfd, evl_new_sem(&c.start, EVL_CLOCK_MONOTONIC, 0, name));
name = get_unique_name(EVL_MONITOR_DEV, 2);
__Tcall_assert(ffd, evl_new_flags(&c.flags, EVL_CLOCK_MONOTONIC, 0, name));
new_thread(&receiver, SCHED_FIFO, LOW_PRIO,
flags_receiver, &c);
__Tcall_assert(ret, evl_put(&c.start));
__Tcall_assert(ret, evl_get(&c.sem));
__Tcall_assert(ret, evl_post_flags(&c.flags, 0x12121212));
__Tcall_assert(ret, evl_peek_flags(&c.flags, &bits));
__Texpr_assert(bits == 0x12121212);
__Tcall_assert(ret, evl_get(&c.sem));
__Tcall_assert(ret, evl_udelay(1000));
__Tcall_assert(ret, evl_post_flags(&c.flags, 0x76767676));
__Tcall_assert(ret, evl_get(&c.sem));
__Texpr_assert(pthread_join(receiver, &status) == 0);
__Texpr_assert(status == NULL);
evl_close_sem(&c.start);
evl_close_sem(&c.sem);
evl_close_flags(&c.flags);
return 0;
}
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