Allow dirty writes selectively

This commit is contained in:
Rodrigo Arias 2023-01-10 18:30:51 +01:00 committed by Rodrigo Arias Mallo
parent e1e0e9662d
commit 0944f84ac9
6 changed files with 263 additions and 55 deletions

View File

@ -106,7 +106,7 @@ bay_init(struct bay *bay)
static int
propagate_chan(struct bay_chan *bchan)
{
dbg("propagating dirty channel %p\n", (void *) bchan);
dbg("- propagating dirty channel %s\n", bchan->chan->name);
struct bay_cb *cur = NULL;
struct bay_cb *tmp = NULL;
@ -123,7 +123,7 @@ propagate_chan(struct bay_chan *bchan)
int
bay_propagate(struct bay *bay)
{
dbg("propagating channels\n");
dbg("-- propagating channels begins\n");
struct bay_chan *cur = NULL;
struct bay_chan *tmp = NULL;
DL_FOREACH_SAFE(bay->dirty, cur, tmp) {
@ -134,8 +134,20 @@ bay_propagate(struct bay *bay)
}
}
/* Flush channels after running all the dirty callbacks, so we
* capture any potential double write when running the
* callbacks */
DL_FOREACH_SAFE(bay->dirty, cur, tmp) {
if (chan_flush(cur->chan) != 0) {
err("bay_propagate: chan_flush failed\n");
return -1;
}
}
bay->dirty = NULL;
dbg("-- propagating channels ends\n");
return 0;
}

View File

@ -36,6 +36,10 @@ static int
set_dirty(struct chan *chan)
{
if (chan->is_dirty) {
/* Already dirty and allowed, no need to do anything */
if (chan->allow_dirty_write)
return 0;
err("channel %s already dirty\n", chan->name);
return -1;
}
@ -44,7 +48,8 @@ set_dirty(struct chan *chan)
if (chan->dirty_cb != NULL) {
if (chan->dirty_cb(chan, chan->dirty_arg) != 0) {
err("dirty callback failed\n");
err("set_dirty %s: dirty callback failed\n",
chan->name);
return -1;
}
}
@ -60,7 +65,8 @@ check_duplicates(struct chan *chan, struct value *v)
// return 0;
if (value_is_equal(&chan->last_value, v)) {
err("check_duplicates: same value as last_value\n");
err("check_duplicates %s: same value as last_value\n",
chan->name);
return -1;
}
@ -71,27 +77,30 @@ int
chan_set(struct chan *chan, struct value value)
{
if (chan->type != CHAN_SINGLE) {
err("chan_set: cannot set on non-single channel\n");
err("chan_set %s: cannot set on non-single channel\n",
chan->name);
return -1;
}
if (chan->is_dirty) {
err("chan_set: cannot modify dirty channel\n");
if (chan->is_dirty && !chan->allow_dirty_write) {
err("chan_set %s: cannot modify dirty channel\n",
chan->name);
return -1;
}
if (check_duplicates(chan, &value) != 0) {
err("chan_set: cannot set a duplicated value\n");
err("chan_set %s: cannot set a duplicated value\n",
chan->name);
return -1;
}
char buf[128];
dbg("chan_set: channel %p sets value %s\n", (void *) chan,
value_str(value, buf));
dbg("chan_set %s: sets value to %s\n",
chan->name, value_str(value, buf));
chan->data.value = value;
if (set_dirty(chan) != 0) {
err("chan_set: set_dirty failed\n");
err("chan_set %s: set_dirty failed\n", chan->name);
return -1;
}
@ -108,31 +117,34 @@ int
chan_push(struct chan *chan, struct value value)
{
if (chan->type != CHAN_STACK) {
err("chan_push: cannot push on non-stack channel\n");
err("chan_push %s: cannot push on non-stack channel\n",
chan->name);
return -1;
}
if (chan->is_dirty) {
err("chan_push: cannot modify dirty channel\n");
if (chan->is_dirty && !chan->allow_dirty_write) {
err("chan_push %s: cannot modify dirty channel\n",
chan->name);
return -1;
}
if (check_duplicates(chan, &value) != 0) {
err("chan_push: cannot push a duplicated value\n");
err("chan_push %s: cannot push a duplicated value\n",
chan->name);
return -1;
}
struct chan_stack *stack = &chan->data.stack;
if (stack->n >= MAX_CHAN_STACK) {
err("chan_push: channel stack full\n");
abort();
err("chan_push %s: channel stack full\n", chan->name);
return -1;
}
stack->values[stack->n++] = value;
if (set_dirty(chan) != 0) {
err("chan_set: set_dirty failed\n");
err("chan_push %s: set_dirty failed\n", chan->name);
return -1;
}
@ -150,34 +162,36 @@ int
chan_pop(struct chan *chan, struct value evalue)
{
if (chan->type != CHAN_STACK) {
err("chan_pop: cannot pop on non-stack channel\n");
err("chan_pop %s: cannot pop on non-stack channel\n",
chan->name);
return -1;
}
if (chan->is_dirty) {
err("chan_pop: cannot modify dirty channel\n");
if (chan->is_dirty && !chan->allow_dirty_write) {
err("chan_pop %s: cannot modify dirty channel\n",
chan->name);
return -1;
}
struct chan_stack *stack = &chan->data.stack;
if (stack->n <= 0) {
err("chan_pop: channel stack empty\n");
err("chan_pop %s: channel stack empty\n", chan->name);
return -1;
}
struct value *value = &stack->values[stack->n - 1];
if (!value_is_equal(value, &evalue)) {
err("chan_pop: unexpected value %ld (expected %ld)\n",
value->i, evalue.i);
err("chan_pop %s: unexpected value %ld (expected %ld)\n",
chan->name, value->i, evalue.i);
return -1;
}
stack->n--;
if (set_dirty(chan) != 0) {
err("chan_set: set_dirty failed\n");
err("chan_pop %s: set_dirty failed\n", chan->name);
return -1;
}
@ -194,7 +208,7 @@ get_value(struct chan *chan, struct value *value)
struct chan_stack *stack = &chan->data.stack;
if (stack->n <= 0) {
err("get_value: channel stack empty\n");
err("get_value %s: channel stack empty\n", chan->name);
return -1;
}
@ -208,9 +222,29 @@ int
chan_read(struct chan *chan, struct value *value)
{
if (get_value(chan, value) != 0) {
err("chan_read: get_value failed\n");
err("chan_read %s: get_value failed\n", chan->name);
return -1;
}
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);
return -1;
}
chan->is_dirty = 0;
return 0;
}
void
chan_dirty_write(struct chan *chan, int allow)
{
chan->allow_dirty_write = allow;
}

View File

@ -35,6 +35,7 @@ struct chan {
enum chan_type type;
union chan_data data;
int is_dirty;
int allow_dirty_write;
struct value err_value;
struct value last_value;
chan_cb_t dirty_cb;
@ -51,6 +52,8 @@ int chan_push(struct chan *chan, struct value value);
int chan_pop(struct chan *chan, struct value expected);
int chan_read(struct chan *chan, struct value *value);
enum chan_type chan_get_type(struct chan *chan);
int chan_flush(struct chan *chan);
void chan_dirty_write(struct chan *chan, int allow);
/* Called when it becomes dirty */
void chan_set_dirty_cb(struct chan *chan, chan_cb_t func, void *arg);

View File

@ -64,7 +64,8 @@ cb_select(struct chan *sel_chan, void *ptr)
return -1;
}
dbg("selecting mux input %p\n", (void *) input);
dbg("mux selects input key=%s chan=%s\n",
value_str(sel_value, buf), input->chan->name);
/* Set to null by default */
struct value out_value = value_null();
@ -102,8 +103,11 @@ cb_input(struct chan *in_chan, void *ptr)
}
/* Nothing to do, the input is not selected */
if (input == NULL || input->chan != in_chan)
if (input == NULL || input->chan != in_chan) {
dbg("mux: input channel %s changed but not selected\n",
in_chan->name);
return 0;
}
dbg("selected mux input %s changed\n", in_chan->name);
@ -135,6 +139,11 @@ mux_init(struct mux *mux,
return -1;
}
/* The output channel must accept multiple writes in the same
* propagation phase, as we may write to the input and select
* channel at the same time. */
chan_dirty_write(output, 1);
memset(mux, 0, sizeof(struct mux_input));
mux->select = select;
mux->output = output;

View File

@ -2,7 +2,46 @@
#include "common.h"
static void
check_single(void)
test_dirty(void)
{
struct chan chan;
chan_init(&chan, CHAN_SINGLE, "testchan");
if (chan_set(&chan, value_int64(1)) != 0)
die("chan_set failed\n");
/* Now channel is dirty */
if (chan_set(&chan, value_int64(2)) == 0)
die("chan_set didn't fail\n");
if (chan_flush(&chan) != 0)
die("chan_flush failed\n");
chan_dirty_write(&chan, 1);
if (chan_set(&chan, value_int64(3)) != 0)
die("chan_set failed\n");
/* Now is dirty, but it should allow multiple writes */
if (chan_set(&chan, value_int64(4)) != 0)
die("chan_set failed\n");
struct value value;
struct value four = value_int64(4);
if (chan_read(&chan, &value) != 0)
die("chan_read failed\n");
if (!value_is_equal(&value, &four))
die("chan_read returned unexpected value\n");
if (chan_flush(&chan) != 0)
die("chan_flush failed\n");
}
static void
test_single(void)
{
struct chan chan;
struct value one = { .type = VALUE_INT64, .i = 1 };
@ -33,7 +72,7 @@ check_single(void)
}
//static void
//check_stack(void)
//test_stack(void)
//{
// struct chan chan;
//
@ -91,7 +130,8 @@ check_single(void)
int main(void)
{
check_single();
//check_stack();
test_single();
test_dirty();
//test_stack();
return 0;
}

View File

@ -1,7 +1,7 @@
#include "emu/mux.h"
#include "common.h"
#define N 4
#define N 6
//static int
//select_active_thread(struct mux *mux,
@ -39,6 +39,132 @@
// return 0;
//}
static void
check_output(struct mux *mux, struct value expected)
{
struct value out_value = value_null();
if (chan_read(mux->output, &out_value) != 0)
die("chan_read() failed for output channel\n");
if (!value_is_equal(&out_value, &expected)) {
char buf1[128];
char buf2[128];
die("unexpected value found %s in output (expected %s)\n",
value_str(out_value, buf1),
value_str(expected, buf2));
}
err("----- output ok -----\n");
}
static void
test_select(struct mux *mux, int key)
{
if (chan_set(mux->select, value_int64(key)) != 0)
die("chan_set failed\n");
if (bay_propagate(mux->bay) != 0)
die("bay_propagate failed\n");
check_output(mux, value_int64(1000 + key));
}
static void
test_input(struct mux *mux, int key)
{
/* Set the select channel to the selected key */
test_select(mux, key);
int new_value = 2000 + key;
/* Then change that channel */
struct mux_input *mi = mux_find_input(mux, value_int64(key));
if (mi == NULL)
die("mux_find_input failed to locate input %d\n", key);
if (chan_set(mi->chan, value_int64(new_value)) != 0)
die("chan_set failed\n");
if (bay_propagate(mux->bay) != 0)
die("bay_propagate failed\n");
check_output(mux, value_int64(new_value));
}
static void
test_select_and_input(struct mux *mux, int key)
{
/* Set the select channel to the selected key, but don't
* propagate the changes yet */
if (chan_set(mux->select, value_int64(key)) != 0)
die("chan_set failed\n");
int new_value = 2000 + key;
/* Also change that channel */
struct mux_input *mi = mux_find_input(mux, value_int64(key));
if (mi == NULL)
die("mux_find_input failed to locate input %d\n", key);
if (chan_set(mi->chan, value_int64(new_value)) != 0)
die("chan_set failed\n");
/* Write twice to the output */
if (bay_propagate(mux->bay) != 0)
die("bay_propagate failed\n");
check_output(mux, value_int64(new_value));
}
static void
test_input_and_select(struct mux *mux, int key)
{
int new_value = 2000 + key;
/* First change the input */
struct mux_input *mi = mux_find_input(mux, value_int64(key));
if (mi == NULL)
die("mux_find_input failed to locate input %d\n", key);
if (chan_set(mi->chan, value_int64(new_value)) != 0)
die("chan_set failed\n");
/* Then change select */
if (chan_set(mux->select, value_int64(key)) != 0)
die("chan_set failed\n");
/* Write twice to the output */
if (bay_propagate(mux->bay) != 0)
die("bay_propagate failed\n");
check_output(mux, value_int64(new_value));
}
static void
test_mid_propagate(struct mux *mux, int key)
{
int new_value = 2000 + key;
struct mux_input *mi = mux_find_input(mux, value_int64(key));
if (mi == NULL)
die("mux_find_input failed to locate input %d\n", key);
if (chan_set(mi->chan, value_int64(new_value)) != 0)
die("chan_set failed\n");
if (bay_propagate(mux->bay) != 0)
die("bay_propagate failed\n");
if (chan_set(mux->select, value_int64(key)) != 0)
die("chan_set failed\n");
if (bay_propagate(mux->bay) != 0)
die("bay_propagate failed\n");
check_output(mux, value_int64(new_value));
}
int
main(void)
{
@ -81,27 +207,11 @@ main(void)
if (bay_propagate(&bay) != 0)
die("bay_propagate failed\n");
/* Change select channel */
if (chan_set(&select, value_int64(2)) != 0)
die("chan_set failed\n");
/* Propagate values and call the callbacks */
if (bay_propagate(&bay) != 0)
die("bay_propagate failed\n");
struct value out_value = value_null();
if (chan_read(&output, &out_value) != 0)
die("chan_read() failed for output channel\n");
struct value expected_value = value_int64(1002);
if (!value_is_equal(&out_value, &expected_value)) {
char buf1[128];
char buf2[128];
die("unexpected value found %s in output (expected %s)\n",
value_str(out_value, buf1),
value_str(expected_value, buf2));
}
test_select(&mux, 1);
test_input(&mux, 2);
test_select_and_input(&mux, 3);
test_input_and_select(&mux, 4);
test_mid_propagate(&mux, 5);
err("OK\n");