Skip to content

Commit

Permalink
Improve async().
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Sep 16, 2020
1 parent 2b29487 commit 5b45fdc
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 24 deletions.
9 changes: 6 additions & 3 deletions examples/asm-dom/demos/fetch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,17 @@ fetch_country_name(dom::context ctx, readable<std::string> country_code)
{
// This will be invoked to launch the fetch operation whenever necessary
// (i.e., whenever we get a new country code).
auto launcher = [](auto ctx, auto report_result, auto country_code) {
auto launcher = [](auto ctx, auto reporter, auto country_code) {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.onsuccess = handle_fetch_success;
attr.onerror = handle_fetch_failure;
attr.userData = new std::function<void(fetch_result)>(report_result);
attr.userData = new std::function<void(fetch_result)>(
[reporter](fetch_result result) {
reporter.report_success(result);
});
auto url = "https://restcountries.eu/rest/v2/alpha/" + country_code;
emscripten_fetch(&attr, url.c_str());
};
Expand Down Expand Up @@ -103,4 +106,4 @@ init_demo(std::string dom_id)

static demo the_demo("fetch-country", init_demo);

} // namespace country_fetch
} // namespace country_fetch
8 changes: 4 additions & 4 deletions examples/asm-dom/demos/timing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ dom::button(ctx, "++", ++n);
dom::text(ctx, "Here's a flickering signal carrying N * 2:");
dom::text(ctx,
alia::async<int>(ctx,
[](auto ctx, auto report_result, auto n) {
[](auto ctx, auto reporter, auto n) {
web::async_call([=]() {
report_result(n * 2);
reporter.report_success(n * 2);
}, 200);
},
n));
Expand Down Expand Up @@ -179,9 +179,9 @@ dom::text(ctx, "Here's the same signal from above with deflickering:");
dom::text(ctx,
deflicker(ctx,
alia::async<int>(ctx,
[](auto ctx, auto report_result, auto n) {
[](auto ctx, auto reporter, auto n) {
web::async_call([=]() {
report_result(n * 2);
reporter.report_success(n * 2);
}, 200);
},
n)));
Expand Down
61 changes: 45 additions & 16 deletions src/alia/signals/async.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ enum class async_status
template<class Value>
struct async_operation_data
{
async_status status = async_status::UNREADY;
// This is used to identify the result of the operation. It's incremented
// every time the inputs change.
counter_type version = 0;
// If status is COMPLETE, this is the result.
Value result;
async_status status = async_status::UNREADY;
// If status is FAILED, this is the error.
std::exception_ptr error;
};

template<class Value>
Expand Down Expand Up @@ -102,6 +107,36 @@ process_async_args(
template<class Result>
struct async_reporter
{
void
report_success(Result result)
{
auto& data = *data_;
if (data.version == version_)
{
data.result = std::move(result);
data.status = async_status::COMPLETE;
mark_dirty_component(container_);
refresh_system(*system_);
}
}

void
report_failure(std::exception_ptr error)
{
auto& data = *data_;
if (data.version == version_)
{
data.status = async_status::FAILED;
data.error = error;
mark_dirty_component(container_);
refresh_system(*system_);
}
}

std::shared_ptr<async_operation_data<Result>> data_;
counter_type version_;
alia::system* system_;
component_container_ptr container_;
};

template<class Result, class Context, class Launcher, class... Args>
Expand All @@ -120,30 +155,24 @@ async(Context ctx, Launcher launcher, Args const&... args)
on_refresh(ctx, [&](auto ctx) {
if (data.status == async_status::UNREADY && args_ready)
{
auto* system = &get<system_tag>(ctx);
auto version = data.version;
auto container = get_active_component_container(ctx);
auto report_result
= [system, version, container, data_ptr](Result result) {
auto& data = *data_ptr;
if (data.version == version)
{
data.result = std::move(result);
data.status = async_status::COMPLETE;
mark_dirty_component(container);
refresh_system(*system);
}
};
try
{
launcher(ctx, report_result, read_signal(args)...);
async_reporter<Result> reporter;
reporter.system_ = &get<system_tag>(ctx);
reporter.version_ = data.version;
reporter.container_ = get_active_component_container(ctx);
reporter.data_ = data_ptr;
launcher(ctx, reporter, read_signal(args)...);
data.status = async_status::LAUNCHED;
}
catch (...)
{
data.error = std::current_exception();
data.status = async_status::FAILED;
}
}
if (data.status == async_status::FAILED)
std::rethrow_exception(data.error);
});

return make_async_signal(data);
Expand Down
2 changes: 1 addition & 1 deletion unit_tests/signals/application.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#define ALIA_LOWERCASE_MACROS

#include <alia/flow/try_catch.hpp>
#include <alia/signals/application.hpp>

#include <testing.hpp>

#include <alia/flow/try_catch.hpp>
#include <alia/signals/basic.hpp>

#include <flow/testing.hpp>
Expand Down
70 changes: 70 additions & 0 deletions unit_tests/signals/async.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <alia/signals/async.hpp>

#include <testing.hpp>

#include <alia/flow/try_catch.hpp>
#include <alia/signals/basic.hpp>
#include <alia/signals/text.hpp>

#include <traversal.hpp>

using namespace alia;

TEST_CASE("async", "[signals][async]")
{
alia::system sys;
initialize_system(sys, [](context) {});

async_reporter<int> reporter;

auto make_controller = [&](int x) {
return [=, &reporter](context ctx) {
ALIA_TRY
{
do_text(
ctx,
printf(
ctx,
"%d",
add_default(
async<int>(
ctx,
[&](auto, auto r, auto x) {
if (x < 0)
throw 2;
else
reporter = r;
},
value(x)),
0)));
}
ALIA_CATCH(...)
{
do_text(ctx, value("(error)"));
}
ALIA_END
};
};

check_traversal(sys, make_controller(-1), "(error);");
check_traversal(sys, make_controller(0), "0;");
reporter.report_success(1);
auto old_reporter = reporter;
check_traversal(sys, make_controller(0), "1;");
check_traversal(sys, make_controller(1), "0;");
try
{
throw 7;
}
catch (...)
{
reporter.report_failure(std::current_exception());
}
check_traversal(sys, make_controller(1), "(error);");
check_traversal(sys, make_controller(1), "(error);");
old_reporter.report_success(1);
check_traversal(sys, make_controller(1), "(error);");
check_traversal(sys, make_controller(2), "0;");
old_reporter.report_success(1);
check_traversal(sys, make_controller(2), "0;");
}

0 comments on commit 5b45fdc

Please sign in to comment.