Commit c9aa5463 authored by Dominik Charousset's avatar Dominik Charousset

Reduce redundancies in flow operators

parent bd86505d
...@@ -292,6 +292,7 @@ caf_add_component( ...@@ -292,6 +292,7 @@ caf_add_component(
flow.error flow.error
flow.flat_map flow.flat_map
flow.for_each flow.for_each
flow.generation
flow.interval flow.interval
flow.merge flow.merge
flow.never flow.never
......
This diff is collapsed.
...@@ -163,8 +163,8 @@ public: ...@@ -163,8 +163,8 @@ public:
return observable<int64_t>{std::move(ptr)}; return observable<int64_t>{std::move(ptr)};
} }
/// Creates an @ref observable without any values that simply calls /// Creates an @ref observable without any values that calls `on_complete`
/// `on_complete` after subscribing to it. /// after subscribing to it.
template <class T> template <class T>
[[nodiscard]] observable<T> empty() { [[nodiscard]] observable<T> empty() {
return observable<T>{make_counted<empty_observable_impl<T>>(ctx_)}; return observable<T>{make_counted<empty_observable_impl<T>>(ctx_)};
...@@ -176,7 +176,8 @@ public: ...@@ -176,7 +176,8 @@ public:
return observable<T>{make_counted<mute_observable_impl<T>>(ctx_)}; return observable<T>{make_counted<mute_observable_impl<T>>(ctx_)};
} }
/// Creates an @ref observable without any values that also never terminates. /// Creates an @ref observable without any values that calls `on_error` after
/// subscribing to it.
template <class T> template <class T>
[[nodiscard]] observable<T> error(caf::error what) { [[nodiscard]] observable<T> error(caf::error what) {
auto ptr = make_counted<observable_error_impl<T>>(ctx_, std::move(what)); auto ptr = make_counted<observable_error_impl<T>>(ctx_, std::move(what));
...@@ -212,6 +213,7 @@ public: ...@@ -212,6 +213,7 @@ public:
template <class Step, class... Steps> template <class Step, class... Steps>
void pull(size_t n, Step& step, Steps&... steps) { void pull(size_t n, Step& step, Steps&... steps) {
CAF_LOG_TRACE(CAF_ARG(n));
while (pos_ != values_.end() && n > 0) { while (pos_ != values_.end() && n > 0) {
if (!step.on_next(*pos_++, steps...)) if (!step.on_next(*pos_++, steps...))
return; return;
...@@ -243,6 +245,7 @@ public: ...@@ -243,6 +245,7 @@ public:
template <class Step, class... Steps> template <class Step, class... Steps>
void pull(size_t n, Step& step, Steps&... steps) { void pull(size_t n, Step& step, Steps&... steps) {
CAF_LOG_TRACE(CAF_ARG(n));
for (size_t i = 0; i < n; ++i) for (size_t i = 0; i < n; ++i)
if (!step.on_next(value_, steps...)) if (!step.on_next(value_, steps...))
return; return;
...@@ -268,7 +271,9 @@ public: ...@@ -268,7 +271,9 @@ public:
value_source& operator=(const value_source&) = default; value_source& operator=(const value_source&) = default;
template <class Step, class... Steps> template <class Step, class... Steps>
void pull(size_t, Step& step, Steps&... steps) { void pull([[maybe_unused]] size_t n, Step& step, Steps&... steps) {
CAF_LOG_TRACE(CAF_ARG(n));
CAF_ASSERT(n > 0);
if (step.on_next(value_, steps...)) if (step.on_next(value_, steps...))
step.on_complete(steps...); step.on_complete(steps...);
} }
...@@ -296,6 +301,7 @@ public: ...@@ -296,6 +301,7 @@ public:
template <class Step, class... Steps> template <class Step, class... Steps>
void pull(size_t n, Step& step, Steps&... steps) { void pull(size_t n, Step& step, Steps&... steps) {
CAF_LOG_TRACE(CAF_ARG(n));
for (size_t i = 0; i < n; ++i) for (size_t i = 0; i < n; ++i)
if (!step.on_next(fn_(), steps...)) if (!step.on_next(fn_(), steps...))
return; return;
...@@ -326,85 +332,46 @@ public: ...@@ -326,85 +332,46 @@ public:
// -- implementation of disposable::impl ----------------------------------- // -- implementation of disposable::impl -----------------------------------
void dispose() override { void dispose() override {
disposed_ = true; term_.dispose();
if (out_) {
out_.on_complete();
out_ = nullptr;
}
} }
bool disposed() const noexcept override { bool disposed() const noexcept override {
return disposed_; return !term_.active();
} }
// -- implementation of observable_impl<T> --------------------------------- // -- implementation of observable_impl<T> ---------------------------------
disposable subscribe(observer<output_type> what) override { disposable subscribe(observer<output_type> sink) override {
if (out_) { CAF_LOG_TRACE(CAF_ARG2("sink", sink.ptr()));
return super::reject_subscription(what, sec::too_many_observers); auto sub = term_.add(this, sink);
} else if (disposed_) { if (sub) {
return super::reject_subscription(what, sec::disposed); term_.start();
} else {
out_ = what;
return super::do_subscribe(what);
} }
return sub;
} }
void on_request(observer_impl<output_type>* sink, size_t n) override { void on_request(observer_impl<output_type>* sink, size_t demand) override {
if (sink == out_.ptr()) { CAF_LOG_TRACE(CAF_ARG(sink) << CAF_ARG(demand));
auto fn = [this, n](auto&... steps) { if (auto n = term_.on_request(sink, demand); n > 0) {
term_step<impl> term{this}; auto fn = [this, n](auto&... steps) { gen_.pull(n, steps..., term_); };
gen_.pull(n, steps..., term);
};
std::apply(fn, steps_); std::apply(fn, steps_);
push(); term_.push();
} }
} }
void on_cancel(observer_impl<output_type>* sink) override { void on_cancel(observer_impl<output_type>* sink) override {
if (sink == out_.ptr()) { CAF_LOG_TRACE(CAF_ARG(sink));
buf_.clear(); if (auto n = term_.on_cancel(sink); n > 0) {
out_ = nullptr; auto fn = [this, n](auto&... steps) { gen_.pull(n, steps..., term_); };
disposed_ = true; std::apply(fn, steps_);
} term_.push();
}
// -- callbacks for term_step ----------------------------------------------
void append_to_buf(const output_type& item) {
CAF_ASSERT(out_.valid());
buf_.emplace_back(item);
}
void shutdown() {
CAF_ASSERT(out_.valid());
push();
out_.on_complete();
out_ = nullptr;
disposed_ = true;
} }
void abort(const error& reason) {
CAF_ASSERT(out_.valid());
push();
out_.on_error(reason);
out_ = nullptr;
disposed_ = true;
} }
private: private:
void push() {
if (!buf_.empty()) {
out_.on_next(make_span(buf_));
buf_.clear();
}
}
Generator gen_; Generator gen_;
std::tuple<Steps...> steps_; std::tuple<Steps...> steps_;
observer<output_type> out_; broadcast_step<output_type> term_;
bool disposed_ = false;
std::vector<output_type> buf_;
}; };
template <class... Ts> template <class... Ts>
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE flow.generation
#include "caf/flow/observable.hpp"
#include "core-test.hpp"
#include "caf/flow/coordinator.hpp"
#include "caf/flow/merge.hpp"
#include "caf/flow/observable_builder.hpp"
#include "caf/flow/observer.hpp"
#include "caf/flow/scoped_coordinator.hpp"
using namespace caf;
namespace {
struct fixture : test_coordinator_fixture<> {
flow::scoped_coordinator_ptr ctx = flow::make_scoped_coordinator();
};
} // namespace
BEGIN_FIXTURE_SCOPE(fixture)
SCENARIO("the repeater source repeats one value indefinitely") {
GIVEN("a repeater source") {
WHEN("subscribing to its output") {
THEN("the observer receives the same value over and over again") {
using ivec = std::vector<int>;
auto snk = flow::make_passive_observer<int>();
ctx->make_observable().repeat(42).subscribe(snk->as_observer());
CHECK_EQ(snk->state, flow::observer_state::subscribed);
CHECK(snk->buf.empty());
if (CHECK(snk->sub)) {
snk->sub.request(3);
ctx->run();
CHECK_EQ(snk->buf, ivec({42, 42, 42}));
snk->sub.request(4);
ctx->run();
CHECK_EQ(snk->buf, ivec({42, 42, 42, 42, 42, 42, 42}));
snk->sub.cancel();
ctx->run();
CHECK_EQ(snk->buf, ivec({42, 42, 42, 42, 42, 42, 42}));
}
}
}
}
}
SCENARIO("the container source streams its input values") {
GIVEN("a container source") {
WHEN("subscribing to its output") {
THEN("the observer receives the values from the container in order") {
using ivec = std::vector<int>;
auto xs = ivec{1, 2, 3, 4, 5, 6, 7};
auto snk = flow::make_passive_observer<int>();
ctx->make_observable().from_container(xs).subscribe(snk->as_observer());
CHECK_EQ(snk->state, flow::observer_state::subscribed);
CHECK(snk->buf.empty());
if (CHECK(snk->sub)) {
snk->sub.request(3);
ctx->run();
CHECK_EQ(snk->buf, ivec({1, 2, 3}));
snk->sub.request(21);
ctx->run();
CHECK_EQ(snk->buf, ivec({1, 2, 3, 4, 5, 6, 7}));
CHECK_EQ(snk->state, flow::observer_state::completed);
}
}
}
}
}
END_FIXTURE_SCOPE()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment