Skip to content

Commit

Permalink
Refine value change semantics.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Dec 10, 2020
1 parent 7fb600c commit ad21df9
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 37 deletions.
121 changes: 88 additions & 33 deletions src/alia/flow/events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ on_init(context ctx, action<> on_init);
void
on_activate(context ctx, action<> on_activate);

namespace detail {

template<class Value>
struct value_change_detection_data
{
Expand All @@ -255,77 +257,130 @@ struct value_change_detection_data
Value value;
};

template<class Signal>
template<class Value, class Signal>
void
on_value_change(context ctx, Signal signal, action<> on_change)
common_value_change_logic(
context ctx,
value_change_detection_data<Value>& data,
Signal const& signal,
action<> on_change)
{
value_change_detection_data<typename Signal::value_type>* data;
if (get_data(ctx, &data))
{
data->has_value = signal_has_value(signal);
if (data->has_value)
data->value = read_signal(signal);
}
refresh_signal_view(
data->id,
data.id,
signal,
[&](auto const& new_value) {
if (!data->has_value || data->value != new_value)
if (!data.has_value || data.value != new_value)
{
isolate_errors(ctx, [&] { perform_action(on_change); });
mark_dirty_component(ctx);
data->has_value = true;
data->value = new_value;
data.has_value = true;
data.value = new_value;
}
},
[&] {
if (data->has_value)
if (data.has_value)
{
isolate_errors(ctx, [&] { perform_action(on_change); });
mark_dirty_component(ctx);
data->has_value = false;
data.has_value = false;
}
});
}

} // namespace detail

template<class Signal>
void
on_value_gain(context ctx, Signal signal, action<> on_gain)
on_value_change(context ctx, Signal const& signal, action<> on_change)
{
bool* saved_state;
if (get_data(ctx, &saved_state))
*saved_state = true;
if (is_refresh_event(ctx))
detail::value_change_detection_data<typename Signal::value_type>* data;
if (get_cached_data(ctx, &data))
{
bool current_state = signal_has_value(signal);
if (current_state && !*saved_state)
{
isolate_errors(ctx, [&] { perform_action(on_gain); });
mark_dirty_component(ctx);
}
*saved_state = current_state;
isolate_errors(ctx, [&] { perform_action(on_change); });
mark_dirty_component(ctx);
data->has_value = signal_has_value(signal);
if (data->has_value)
data->value = read_signal(signal);
}
detail::common_value_change_logic(ctx, *data, signal, on_change);
}

template<class Signal>
void
on_value_loss(context ctx, Signal signal, action<> on_loss)
on_observed_value_change(context ctx, Signal const& signal, action<> on_change)
{
detail::value_change_detection_data<typename Signal::value_type>* data;
if (get_cached_data(ctx, &data))
{
data->has_value = signal_has_value(signal);
if (data->has_value)
data->value = read_signal(signal);
}
detail::common_value_change_logic(ctx, *data, signal, on_change);
}

namespace detail {

template<bool TriggeringState, class Signal>
void
common_value_edge_logic(
context ctx, bool* saved_state, Signal const& signal, action<> on_edge)
{
bool* saved_state;
if (get_data(ctx, &saved_state))
*saved_state = false;
if (is_refresh_event(ctx))
{
bool current_state = signal_has_value(signal);
if (!current_state && *saved_state)
if (current_state == TriggeringState
&& *saved_state != TriggeringState)
{
isolate_errors(ctx, [&] { perform_action(on_loss); });
isolate_errors(ctx, [&] { perform_action(on_edge); });
mark_dirty_component(ctx);
}
*saved_state = current_state;
}
}

} // namespace detail

template<class Signal>
void
on_value_gain(context ctx, Signal const& signal, action<> on_gain)
{
bool* saved_state;
if (get_cached_data(ctx, &saved_state))
*saved_state = false;
detail::common_value_edge_logic<true>(ctx, saved_state, signal, on_gain);
}

template<class Signal>
void
on_observed_value_gain(context ctx, Signal const& signal, action<> on_gain)
{
bool* saved_state;
if (get_cached_data(ctx, &saved_state))
*saved_state = true;
detail::common_value_edge_logic<true>(ctx, saved_state, signal, on_gain);
}

template<class Signal>
void
on_value_loss(context ctx, Signal const& signal, action<> on_loss)
{
bool* saved_state;
if (get_cached_data(ctx, &saved_state))
*saved_state = true;
detail::common_value_edge_logic<false>(ctx, saved_state, signal, on_loss);
}

template<class Signal>
void
on_observed_value_loss(context ctx, Signal const& signal, action<> on_loss)
{
bool* saved_state;
if (get_cached_data(ctx, &saved_state))
*saved_state = false;
detail::common_value_edge_logic<false>(ctx, saved_state, signal, on_loss);
}

} // namespace alia

#endif
64 changes: 60 additions & 4 deletions unit_tests/flow/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ TEST_CASE("on_init/on_activate", "[flow][events]")
REQUIRE(activate_count == 3);
}

TEST_CASE("on_value events", "[flow][events]")
TEST_CASE("on_observed_value events", "[flow][events]")
{
bool has_value = false;
int value = 0;
Expand All @@ -184,9 +184,9 @@ TEST_CASE("on_value events", "[flow][events]")
auto signal
= lambda_reader([&] { return has_value; }, [&] { return value; });

on_value_gain(ctx, signal, ++direct(gain_count));
on_value_loss(ctx, signal, ++direct(loss_count));
on_value_change(ctx, signal, ++direct(change_count));
on_observed_value_gain(ctx, signal, ++direct(gain_count));
on_observed_value_loss(ctx, signal, ++direct(loss_count));
on_observed_value_change(ctx, signal, ++direct(change_count));
});

refresh_system(sys);
Expand Down Expand Up @@ -275,6 +275,62 @@ TEST_CASE("on_value events", "[flow][events]")
REQUIRE(change_shadow == 4);
}

TEST_CASE("on_value events", "[flow][events]")
{
bool has_value = false;
int value = 0;

int gain_count = 0;
int gain_shadow = 0;

int loss_count = 0;
int loss_shadow = 0;

int change_count = 0;
int change_shadow = 0;

alia::system sys;
initialize_system(sys, [&](context ctx) {
on_refresh(ctx, [&](auto) {
gain_shadow = gain_count;
loss_shadow = loss_count;
change_shadow = change_count;
});

auto signal
= lambda_reader([&] { return has_value; }, [&] { return value; });

on_value_gain(ctx, signal, ++direct(gain_count));
on_value_loss(ctx, signal, ++direct(loss_count));
on_value_change(ctx, signal, ++direct(change_count));
});

refresh_system(sys);
REQUIRE(gain_count == 0);
REQUIRE(gain_shadow == 0);
REQUIRE(loss_count == 1);
REQUIRE(loss_shadow == 1);
REQUIRE(change_count == 1);
REQUIRE(change_shadow == 1);

refresh_system(sys);
REQUIRE(gain_count == 0);
REQUIRE(gain_shadow == 0);
REQUIRE(loss_count == 1);
REQUIRE(loss_shadow == 1);
REQUIRE(change_count == 1);
REQUIRE(change_shadow == 1);

has_value = true;
refresh_system(sys);
REQUIRE(gain_count == 1);
REQUIRE(gain_shadow == 1);
REQUIRE(loss_count == 1);
REQUIRE(loss_shadow == 1);
REQUIRE(change_count == 2);
REQUIRE(change_shadow == 2);
}

TEST_CASE("error isolation", "[flow][events]")
{
int n = 0;
Expand Down

0 comments on commit ad21df9

Please sign in to comment.