Allow dirty writes selectively
This commit is contained in:
parent
e1e0e9662d
commit
0944f84ac9
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
154
test/unit/mux.c
154
test/unit/mux.c
@ -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");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user