2023-01-12 19:16:52 +01:00
|
|
|
/* Copyright (c) 2021-2022 Barcelona Supercomputing Center (BSC)
|
2022-09-19 12:39:02 +02:00
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
2021-10-26 18:42:41 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
#include "chan.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include <string.h>
|
2021-10-18 12:47:51 +02:00
|
|
|
|
2021-10-21 16:41:52 +02:00
|
|
|
void
|
2023-01-12 19:16:52 +01:00
|
|
|
chan_init(struct chan *chan, enum chan_type type, const char *name)
|
2021-10-21 16:41:52 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
int len = strlen(name);
|
|
|
|
if (len >= MAX_CHAN_NAME)
|
|
|
|
die("chan_init: name '%s' too long\n", name);
|
|
|
|
|
|
|
|
memset(chan, 0, sizeof(*chan));
|
|
|
|
memcpy(chan->name, name, len + 1);
|
|
|
|
chan->type = type;
|
2021-10-21 16:41:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-01-12 19:16:52 +01:00
|
|
|
chan_set_dirty_cb(struct chan *chan, chan_cb_t func, void *arg)
|
2021-10-21 16:41:52 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
chan->dirty_cb = func;
|
|
|
|
chan->dirty_arg = arg;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
enum chan_type
|
|
|
|
chan_get_type(struct chan *chan)
|
2021-10-18 12:47:51 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
return chan->type;
|
2021-10-18 12:47:51 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
static int
|
|
|
|
set_dirty(struct chan *chan)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
if (chan->is_dirty) {
|
2023-01-10 18:30:51 +01:00
|
|
|
/* Already dirty and allowed, no need to do anything */
|
2023-01-11 11:58:23 +01:00
|
|
|
if (chan->prop[CHAN_DIRTY_WRITE])
|
2023-01-10 18:30:51 +01:00
|
|
|
return 0;
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
err("channel %s already dirty\n", chan->name);
|
|
|
|
return -1;
|
2021-10-21 16:41:52 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
chan->is_dirty = 1;
|
2021-10-18 12:47:51 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (chan->dirty_cb != NULL) {
|
|
|
|
if (chan->dirty_cb(chan, chan->dirty_arg) != 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("set_dirty %s: dirty callback failed\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2021-10-18 12:47:51 +02:00
|
|
|
}
|
2021-10-21 16:15:29 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
return 0;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
static int
|
|
|
|
check_duplicates(struct chan *chan, struct value *v)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
/* If duplicates are allowed just skip the check */
|
2023-01-11 11:58:23 +01:00
|
|
|
if (chan->prop[CHAN_DUPLICATES])
|
|
|
|
return 0;
|
2021-10-21 16:15:29 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (value_is_equal(&chan->last_value, v)) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("check_duplicates %s: same value as last_value\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-11-19 16:22:37 +01:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
return 0;
|
2021-10-18 12:47:51 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
int
|
|
|
|
chan_set(struct chan *chan, struct value value)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
if (chan->type != CHAN_SINGLE) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_set %s: cannot set on non-single channel\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-11-19 16:22:37 +01:00
|
|
|
}
|
|
|
|
|
2023-01-11 11:58:23 +01:00
|
|
|
if (chan->is_dirty && !chan->prop[CHAN_DIRTY_WRITE]) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_set %s: cannot modify dirty channel\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-11-19 16:22:37 +01:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (check_duplicates(chan, &value) != 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_set %s: cannot set a duplicated value\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-13 11:27:13 +01:00
|
|
|
//char buf[128];
|
|
|
|
//dbg("chan_set %s: sets value to %s\n",
|
|
|
|
// chan->name, value_str(value, buf));
|
2023-01-12 19:16:52 +01:00
|
|
|
chan->data.value = value;
|
2021-10-18 12:47:51 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (set_dirty(chan) != 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_set %s: set_dirty failed\n", chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
/** Adds one value to the stack. Fails if the stack is full.
|
|
|
|
*
|
|
|
|
* @param ivalue The new integer value to be added on the stack.
|
|
|
|
*
|
|
|
|
* @return On success returns 0, otherwise returns -1.
|
|
|
|
*/
|
2021-10-21 16:15:29 +02:00
|
|
|
int
|
2023-01-12 19:16:52 +01:00
|
|
|
chan_push(struct chan *chan, struct value value)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
if (chan->type != CHAN_STACK) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_push %s: cannot push on non-stack channel\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-11 11:58:23 +01:00
|
|
|
if (chan->is_dirty && !chan->prop[CHAN_DIRTY_WRITE]) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_push %s: cannot modify dirty channel\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (check_duplicates(chan, &value) != 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_push %s: cannot push a duplicated value\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-11-19 16:22:37 +01:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
struct chan_stack *stack = &chan->data.stack;
|
|
|
|
|
|
|
|
if (stack->n >= MAX_CHAN_STACK) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_push %s: channel stack full\n", chan->name);
|
|
|
|
return -1;
|
2021-11-19 16:22:37 +01:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
stack->values[stack->n++] = value;
|
2021-10-21 16:15:29 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (set_dirty(chan) != 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_push %s: set_dirty failed\n", chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2021-10-18 12:47:51 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
return 0;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
/** Remove one value from the stack. Fails if the top of the stack
|
|
|
|
* doesn't match the expected value.
|
|
|
|
*
|
|
|
|
* @param expected The expected value on the top of the stack.
|
|
|
|
*
|
|
|
|
* @return On success returns 0, otherwise returns -1.
|
|
|
|
*/
|
2021-10-21 16:15:29 +02:00
|
|
|
int
|
2023-01-12 19:16:52 +01:00
|
|
|
chan_pop(struct chan *chan, struct value evalue)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
if (chan->type != CHAN_STACK) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_pop %s: cannot pop on non-stack channel\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2021-10-21 16:15:29 +02:00
|
|
|
|
2023-01-11 11:58:23 +01:00
|
|
|
if (chan->is_dirty && !chan->prop[CHAN_DIRTY_WRITE]) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_pop %s: cannot modify dirty channel\n",
|
|
|
|
chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2022-10-03 10:51:58 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
struct chan_stack *stack = &chan->data.stack;
|
2022-10-03 10:51:58 +02:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (stack->n <= 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_pop %s: channel stack empty\n", chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2022-10-03 10:51:58 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
struct value *value = &stack->values[stack->n - 1];
|
|
|
|
|
|
|
|
if (!value_is_equal(value, &evalue)) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_pop %s: unexpected value %ld (expected %ld)\n",
|
|
|
|
chan->name, value->i, evalue.i);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-11-17 12:08:25 +01:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
stack->n--;
|
2021-11-17 12:08:25 +01:00
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
if (set_dirty(chan) != 0) {
|
2023-01-10 18:30:51 +01:00
|
|
|
err("chan_pop %s: set_dirty failed\n", chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-11-17 12:08:25 +01:00
|
|
|
}
|
2023-01-12 19:16:52 +01:00
|
|
|
|
|
|
|
return 0;
|
2021-11-17 12:08:25 +01:00
|
|
|
}
|
|
|
|
|
2023-01-11 11:58:23 +01:00
|
|
|
static void
|
2023-01-12 19:16:52 +01:00
|
|
|
get_value(struct chan *chan, struct value *value)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-12 19:16:52 +01:00
|
|
|
if (chan->type == CHAN_SINGLE) {
|
|
|
|
*value = chan->data.value;
|
2023-01-11 11:58:23 +01:00
|
|
|
} else {
|
|
|
|
struct chan_stack *stack = &chan->data.stack;
|
|
|
|
if (stack->n > 0)
|
|
|
|
*value = stack->values[stack->n - 1];
|
|
|
|
else
|
|
|
|
*value = value_null();
|
2023-01-12 19:16:52 +01:00
|
|
|
}
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
/** Reads the current value of a channel */
|
|
|
|
int
|
|
|
|
chan_read(struct chan *chan, struct value *value)
|
2021-10-21 16:15:29 +02:00
|
|
|
{
|
2023-01-11 11:58:23 +01:00
|
|
|
get_value(chan, value);
|
2023-01-10 18:30:51 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove the dirty state */
|
|
|
|
int
|
|
|
|
chan_flush(struct chan *chan)
|
|
|
|
{
|
|
|
|
if (!chan->is_dirty) {
|
|
|
|
err("chan_flush %s: channel is not dirty\n", chan->name);
|
2023-01-12 19:16:52 +01:00
|
|
|
return -1;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
|
|
|
|
2023-01-11 11:58:23 +01:00
|
|
|
get_value(chan, &chan->last_value);
|
|
|
|
|
2023-01-10 18:30:51 +01:00
|
|
|
chan->is_dirty = 0;
|
|
|
|
|
2023-01-12 19:16:52 +01:00
|
|
|
return 0;
|
2021-10-21 16:15:29 +02:00
|
|
|
}
|
2023-01-10 18:30:51 +01:00
|
|
|
|
|
|
|
void
|
2023-01-11 11:58:23 +01:00
|
|
|
chan_prop_set(struct chan *chan, enum chan_prop prop, int value)
|
|
|
|
{
|
|
|
|
chan->prop[prop] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
chan_prop_get(struct chan *chan, enum chan_prop prop)
|
2023-01-10 18:30:51 +01:00
|
|
|
{
|
2023-01-11 11:58:23 +01:00
|
|
|
return chan->prop[prop];
|
2023-01-10 18:30:51 +01:00
|
|
|
}
|