Commit 3c388a9c authored by Dominik Charousset's avatar Dominik Charousset

Clean up the observable interface

The `observable` interface should only have one member functions:
`subscribe`. Hence, we move the `on_request` and `on_cancel` member
functions to the new `subscription::listener` class. Also adds a new
default implementation type for subscriptions that no longer requires a
template.
parent d9842dba
...@@ -130,4 +130,7 @@ private: ...@@ -130,4 +130,7 @@ private:
intrusive_ptr<impl> pimpl_; intrusive_ptr<impl> pimpl_;
}; };
/// @relates disposable
using disposable_impl = disposable::impl;
} // namespace caf } // namespace caf
This diff is collapsed.
...@@ -32,14 +32,13 @@ class callable_source; ...@@ -32,14 +32,13 @@ class callable_source;
class interval_action; class interval_action;
class CAF_CORE_EXPORT interval_impl : public ref_counted, class CAF_CORE_EXPORT interval_impl : public observable_impl_base<int64_t> {
public observable_impl<int64_t> {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using output_type = int64_t; using output_type = int64_t;
using super = observable_impl<int64_t>; using super = observable_impl_base<int64_t>;
// -- friends ---------------------------------------------------------------- // -- friends ----------------------------------------------------------------
...@@ -54,32 +53,23 @@ public: ...@@ -54,32 +53,23 @@ public:
interval_impl(coordinator* ctx, timespan initial_delay, timespan period, interval_impl(coordinator* ctx, timespan initial_delay, timespan period,
int64_t max_val); int64_t max_val);
~interval_impl() override;
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable::impl -------------------------------------
void dispose() override; void dispose() override;
bool disposed() const noexcept override; bool disposed() const noexcept override;
void ref_disposable() const noexcept override;
void deref_disposable() const noexcept override;
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
coordinator* ctx() const noexcept override; void on_request(disposable_impl*, size_t) override;
void on_request(observer_impl<int64_t>*, size_t) override; void on_cancel(disposable_impl*) override;
void on_cancel(observer_impl<int64_t>*) override;
disposable subscribe(observer<int64_t> what) override; disposable subscribe(observer<int64_t> what) override;
private: private:
void fire(interval_action*); void fire(interval_action*);
coordinator* ctx_;
observer<int64_t> obs_; observer<int64_t> obs_;
disposable pending_; disposable pending_;
timespan initial_delay_; timespan initial_delay_;
...@@ -364,7 +354,7 @@ public: ...@@ -364,7 +354,7 @@ public:
return sub; return sub;
} }
void on_request(observer_impl<output_type>* sink, size_t demand) override { void on_request(disposable_impl* sink, size_t demand) override {
CAF_LOG_TRACE(CAF_ARG(sink) << CAF_ARG(demand)); CAF_LOG_TRACE(CAF_ARG(sink) << CAF_ARG(demand));
if (auto n = term_.on_request(sink, demand); n > 0) { if (auto n = term_.on_request(sink, demand); n > 0) {
auto fn = [this, n](auto&... steps) { gen_.pull(n, steps..., term_); }; auto fn = [this, n](auto&... steps) { gen_.pull(n, steps..., term_); };
...@@ -373,7 +363,7 @@ public: ...@@ -373,7 +363,7 @@ public:
} }
} }
void on_cancel(observer_impl<output_type>* sink) override { void on_cancel(disposable_impl* sink) override {
CAF_LOG_TRACE(CAF_ARG(sink)); CAF_LOG_TRACE(CAF_ARG(sink));
if (auto n = term_.on_cancel(sink); n > 0) { if (auto n = term_.on_cancel(sink); n > 0) {
auto fn = [this, n](auto&... steps) { gen_.pull(n, steps..., term_); }; auto fn = [this, n](auto&... steps) { gen_.pull(n, steps..., term_); };
......
...@@ -25,20 +25,16 @@ public: ...@@ -25,20 +25,16 @@ public:
using output_type = T; using output_type = T;
/// Internal interface of a `single`. /// Internal interface of a `single`.
class impl : public ref_counted, public observable_impl<T> { class impl : public observable_impl_base<T> {
public: public:
using super = observable_impl<T>; using super = observable_impl_base<T>;
CAF_INTRUSIVE_PTR_FRIENDS(impl) CAF_INTRUSIVE_PTR_FRIENDS(impl)
explicit impl(coordinator* ctx) : ctx_(ctx) { explicit impl(coordinator* ctx) : super(ctx) {
// nop // nop
} }
coordinator* ctx() const noexcept override {
return ctx_;
}
disposable subscribe(observer<T> what) override { disposable subscribe(observer<T> what) override {
if (!std::holds_alternative<error>(value_)) { if (!std::holds_alternative<error>(value_)) {
auto res = super::do_subscribe(what.ptr()); auto res = super::do_subscribe(what.ptr());
...@@ -50,7 +46,7 @@ public: ...@@ -50,7 +46,7 @@ public:
} }
} }
void on_request(observer_impl<T>* sink, size_t n) override { void on_request(disposable_impl* sink, size_t n) override {
auto pred = [sink](auto& entry) { return entry.first.ptr() == sink; }; auto pred = [sink](auto& entry) { return entry.first.ptr() == sink; };
if (auto i = std::find_if(observers_.begin(), observers_.end(), pred); if (auto i = std::find_if(observers_.begin(), observers_.end(), pred);
i != observers_.end()) { i != observers_.end()) {
...@@ -69,7 +65,7 @@ public: ...@@ -69,7 +65,7 @@ public:
} }
} }
void on_cancel(observer_impl<T>* sink) override { void on_cancel(disposable_impl* sink) override {
auto pred = [sink](auto& entry) { return entry.first.ptr() == sink; }; auto pred = [sink](auto& entry) { return entry.first.ptr() == sink; };
if (auto i = std::find_if(observers_.begin(), observers_.end(), pred); if (auto i = std::find_if(observers_.begin(), observers_.end(), pred);
i != observers_.end()) i != observers_.end())
...@@ -85,14 +81,6 @@ public: ...@@ -85,14 +81,6 @@ public:
return observers_.empty() && !std::holds_alternative<none_t>(value_); return observers_.empty() && !std::holds_alternative<none_t>(value_);
} }
void ref_disposable() const noexcept final {
this->ref();
}
void deref_disposable() const noexcept final {
this->deref();
}
void set_value(T val) { void set_value(T val) {
if (std::holds_alternative<none_t>(value_)) { if (std::holds_alternative<none_t>(value_)) {
value_ = std::move(val); value_ = std::move(val);
...@@ -119,7 +107,6 @@ public: ...@@ -119,7 +107,6 @@ public:
} }
private: private:
coordinator* ctx_;
std::variant<none_t, T, error> value_; std::variant<none_t, T, error> value_;
std::vector<std::pair<observer<T>, size_t>> observers_; std::vector<std::pair<observer<T>, size_t>> observers_;
}; };
......
...@@ -410,7 +410,7 @@ public: ...@@ -410,7 +410,7 @@ public:
/// Requests `n` more items for `sink`. /// Requests `n` more items for `sink`.
/// @returns New demand to signal upstream or 0. /// @returns New demand to signal upstream or 0.
/// @note Calls @ref push. /// @note Calls @ref push.
size_t on_request(observer_impl_t* sink, size_t n) { size_t on_request(disposable_impl* sink, size_t n) {
if (auto i = find(sink); i != outputs_.end()) { if (auto i = find(sink); i != outputs_.end()) {
i->demand += n; i->demand += n;
push(); push();
...@@ -422,7 +422,7 @@ public: ...@@ -422,7 +422,7 @@ public:
/// Requests `n` more items for `sink`. /// Requests `n` more items for `sink`.
/// @note Calls @ref push and may call `sub.request(n)`. /// @note Calls @ref push and may call `sub.request(n)`.
void on_request(subscription& sub, observer_impl_t* sink, size_t n) { void on_request(subscription& sub, disposable_impl* sink, size_t n) {
if (auto new_demand = on_request(sink, n); new_demand > 0 && sub) if (auto new_demand = on_request(sink, n); new_demand > 0 && sub)
sub.request(new_demand); sub.request(new_demand);
} }
...@@ -430,7 +430,7 @@ public: ...@@ -430,7 +430,7 @@ public:
/// Removes `sink` from the observer set. /// Removes `sink` from the observer set.
/// @returns New demand to signal upstream or 0. /// @returns New demand to signal upstream or 0.
/// @note Calls @ref push. /// @note Calls @ref push.
size_t on_cancel(observer_impl_t* sink) { size_t on_cancel(disposable_impl* sink) {
if (auto i = find(sink); i != outputs_.end()) { if (auto i = find(sink); i != outputs_.end()) {
outputs_.erase(i); outputs_.erase(i);
// TODO: shut down on last cancel? // TODO: shut down on last cancel?
...@@ -443,7 +443,7 @@ public: ...@@ -443,7 +443,7 @@ public:
/// Requests `n` more items for `sink`. /// Requests `n` more items for `sink`.
/// @note Calls @ref push and may call `sub.request(n)`. /// @note Calls @ref push and may call `sub.request(n)`.
void on_cancel(subscription& sub, observer_impl_t* sink) { void on_cancel(subscription& sub, disposable_impl* sink) {
if (auto new_demand = on_cancel(sink); new_demand > 0 && sub) if (auto new_demand = on_cancel(sink); new_demand > 0 && sub)
sub.request(new_demand); sub.request(new_demand);
} }
...@@ -574,8 +574,7 @@ public: ...@@ -574,8 +574,7 @@ public:
} }
private: private:
typename std::vector<output_t>::iterator typename std::vector<output_t>::iterator find(disposable_impl* sink) {
find(observer_impl<output_type>* sink) {
auto e = outputs_.end(); auto e = outputs_.end();
auto pred = [sink](const output_t& out) { return out.sink.ptr() == sink; }; auto pred = [sink](const output_t& out) { return out.sink.ptr() == sink; };
return std::find_if(outputs_.begin(), e, pred); return std::find_if(outputs_.begin(), e, pred);
......
...@@ -4,13 +4,15 @@ ...@@ -4,13 +4,15 @@
#pragma once #pragma once
#include <cstddef>
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/disposable.hpp" #include "caf/disposable.hpp"
#include "caf/flow/fwd.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/ref_counted.hpp" #include "caf/ref_counted.hpp"
#include <cstddef>
#include <type_traits>
namespace caf::flow { namespace caf::flow {
/// Controls the flow of items from publishers to subscribers. /// Controls the flow of items from publishers to subscribers.
...@@ -18,7 +20,7 @@ class CAF_CORE_EXPORT subscription { ...@@ -18,7 +20,7 @@ class CAF_CORE_EXPORT subscription {
public: public:
// -- nested types ----------------------------------------------------------- // -- nested types -----------------------------------------------------------
/// Internal impl of a `disposable`. /// Internal interface of a `subscription`.
class CAF_CORE_EXPORT impl : public disposable::impl { class CAF_CORE_EXPORT impl : public disposable::impl {
public: public:
~impl() override; ~impl() override;
...@@ -34,8 +36,7 @@ public: ...@@ -34,8 +36,7 @@ public:
}; };
/// A trivial subscription type that drops all member function calls. /// A trivial subscription type that drops all member function calls.
class CAF_CORE_EXPORT nop_impl final : public ref_counted, class CAF_CORE_EXPORT nop_impl final : public ref_counted, public impl {
public subscription::impl {
public: public:
// -- friends -------------------------------------------------------------- // -- friends --------------------------------------------------------------
...@@ -55,6 +56,70 @@ public: ...@@ -55,6 +56,70 @@ public:
bool disposed_ = false; bool disposed_ = false;
}; };
/// Describes a listener to the subscription that will receive an event
/// whenever the observer calls `request` or `cancel`.
class CAF_CORE_EXPORT listener : public disposable::impl {
public:
virtual ~listener();
virtual void on_request(disposable::impl* sink, size_t n) = 0;
virtual void on_cancel(disposable::impl* sink) = 0;
};
/// Default implementation for subscriptions that forward `request` and
/// `cancel` to a @ref listener.
class default_impl final : public ref_counted, public impl {
public:
CAF_INTRUSIVE_PTR_FRIENDS(default_impl)
default_impl(coordinator* ctx, listener* src, disposable::impl* snk)
: ctx_(ctx), src_(src), snk_(snk) {
// nop
}
bool disposed() const noexcept override;
void ref_disposable() const noexcept override;
void deref_disposable() const noexcept override;
void request(size_t n) override;
void cancel() override;
auto* ctx() const noexcept {
return ctx_;
}
/// Creates a new subscription object.
/// @param ctx The owner of @p src and @p snk.
/// @param src The @ref observable that emits items.
/// @param snk the @ref observer that consumes items.
/// @returns an instance of @ref default_impl in a @ref subscription handle.
template <class Observable, class Observer>
static subscription make(coordinator* ctx, Observable* src, Observer* snk) {
static_assert(std::is_base_of_v<listener, Observable>);
static_assert(std::is_base_of_v<disposable_impl, Observer>);
static_assert(std::is_same_v<typename Observable::output_type,
typename Observer::input_type>);
intrusive_ptr<impl> ptr{new default_impl(ctx, src, snk), false};
return subscription{std::move(ptr)};
}
/// Like @ref make but without any type checking.
static subscription make_unsafe(coordinator* ctx, listener* src,
disposable_impl* snk) {
intrusive_ptr<impl> ptr{new default_impl(ctx, src, snk), false};
return subscription{std::move(ptr)};
}
private:
coordinator* ctx_;
intrusive_ptr<listener> src_;
intrusive_ptr<disposable_impl> snk_;
};
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
explicit subscription(intrusive_ptr<impl> pimpl) noexcept explicit subscription(intrusive_ptr<impl> pimpl) noexcept
......
...@@ -41,14 +41,13 @@ struct zipper_input { ...@@ -41,14 +41,13 @@ struct zipper_input {
/// Combines items from any number of observables using a zip function. /// Combines items from any number of observables using a zip function.
template <class F, class... Ts> template <class F, class... Ts>
class zipper_impl : public ref_counted, class zipper_impl : public observable_impl_base<zipper_output_t<F, Ts...>> {
public observable_impl<zipper_output_t<F, Ts...>> {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using output_type = zipper_output_t<F, Ts...>; using output_type = zipper_output_t<F, Ts...>;
using super = observable_impl<output_type>; using super = observable_impl_base<output_type>;
// -- friends ---------------------------------------------------------------- // -- friends ----------------------------------------------------------------
...@@ -60,7 +59,7 @@ public: ...@@ -60,7 +59,7 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
zipper_impl(coordinator* ctx, F fn, observable<Ts>... inputs) zipper_impl(coordinator* ctx, F fn, observable<Ts>... inputs)
: ctx_(ctx), fn_(std::move(fn)), inputs_(inputs...) { : super(ctx), fn_(std::move(fn)), inputs_(inputs...) {
// nop // nop
} }
...@@ -86,21 +85,9 @@ public: ...@@ -86,21 +85,9 @@ public:
return term_.finalized(); return term_.finalized();
} }
void ref_disposable() const noexcept override {
this->ref();
}
void deref_disposable() const noexcept override {
this->deref();
}
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
coordinator* ctx() const noexcept override { void on_request(disposable_impl* sink, size_t demand) override {
return ctx_;
}
void on_request(observer_impl<output_type>* sink, size_t demand) override {
if (auto n = term_.on_request(sink, demand); n > 0) { if (auto n = term_.on_request(sink, demand); n > 0) {
demand_ += n; demand_ += n;
for_each_input([n](auto, auto& input) { for_each_input([n](auto, auto& input) {
...@@ -110,7 +97,7 @@ public: ...@@ -110,7 +97,7 @@ public:
} }
} }
void on_cancel(observer_impl<output_type>* sink) override { void on_cancel(disposable_impl* sink) override {
if (auto n = term_.on_cancel(sink); n > 0) { if (auto n = term_.on_cancel(sink); n > 0) {
demand_ += n; demand_ += n;
for_each_input([n](auto, auto& input) { for_each_input([n](auto, auto& input) {
...@@ -241,7 +228,6 @@ private: ...@@ -241,7 +228,6 @@ private:
term_.fin(); term_.fin();
} }
coordinator* ctx_;
size_t demand_ = 0; size_t demand_ = 0;
F fn_; F fn_;
std::tuple<zipper_input<Ts>...> inputs_; std::tuple<zipper_input<Ts>...> inputs_;
......
...@@ -62,14 +62,10 @@ interval_impl::interval_impl(coordinator* ctx, timespan initial_delay, ...@@ -62,14 +62,10 @@ interval_impl::interval_impl(coordinator* ctx, timespan initial_delay,
interval_impl::interval_impl(coordinator* ctx, timespan initial_delay, interval_impl::interval_impl(coordinator* ctx, timespan initial_delay,
timespan period, int64_t max_val) timespan period, int64_t max_val)
: ctx_(ctx), initial_delay_(initial_delay), period_(period), max_(max_val) { : super(ctx), initial_delay_(initial_delay), period_(period), max_(max_val) {
CAF_ASSERT(max_val > 0); CAF_ASSERT(max_val > 0);
} }
interval_impl::~interval_impl() {
// nop
}
void interval_impl::dispose() { void interval_impl::dispose() {
if (obs_) { if (obs_) {
obs_.on_complete(); obs_.on_complete();
...@@ -86,19 +82,7 @@ bool interval_impl::disposed() const noexcept { ...@@ -86,19 +82,7 @@ bool interval_impl::disposed() const noexcept {
return val_ == max_; return val_ == max_;
} }
void interval_impl::ref_disposable() const noexcept { void interval_impl::on_request(disposable_impl* ptr, size_t n) {
this->ref();
}
void interval_impl::deref_disposable() const noexcept {
this->deref();
}
coordinator* interval_impl::ctx() const noexcept {
return ctx_;
}
void interval_impl::on_request(observer_impl<int64_t>* ptr, size_t n) {
if (obs_.ptr() == ptr) { if (obs_.ptr() == ptr) {
if (demand_ == 0 && !pending_) { if (demand_ == 0 && !pending_) {
if (val_ == 0) if (val_ == 0)
...@@ -112,7 +96,7 @@ void interval_impl::on_request(observer_impl<int64_t>* ptr, size_t n) { ...@@ -112,7 +96,7 @@ void interval_impl::on_request(observer_impl<int64_t>* ptr, size_t n) {
} }
} }
void interval_impl::on_cancel(observer_impl<int64_t>* ptr) { void interval_impl::on_cancel(disposable_impl* ptr) {
if (obs_.ptr() == ptr) { if (obs_.ptr() == ptr) {
obs_ = nullptr; obs_ = nullptr;
pending_.dispose(); pending_.dispose();
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "caf/flow/subscription.hpp" #include "caf/flow/subscription.hpp"
#include "caf/flow/coordinator.hpp"
namespace caf::flow { namespace caf::flow {
subscription::impl::~impl() { subscription::impl::~impl() {
...@@ -34,4 +36,37 @@ void subscription::nop_impl::request(size_t) { ...@@ -34,4 +36,37 @@ void subscription::nop_impl::request(size_t) {
// nop // nop
} }
subscription::listener::~listener() {
// nop
}
bool subscription::default_impl::disposed() const noexcept {
return src_ == nullptr;
}
void subscription::default_impl::ref_disposable() const noexcept {
this->ref();
}
void subscription::default_impl::deref_disposable() const noexcept {
this->deref();
}
void subscription::default_impl::request(size_t n) {
if (src_)
ctx()->delay_fn([src = src_, snk = snk_, n] { //
src->on_request(snk.get(), n);
});
}
void subscription::default_impl::cancel() {
if (src_) {
ctx()->delay_fn([src = src_, snk = snk_] { //
src->on_cancel(snk.get());
});
src_.reset();
snk_.reset();
}
}
} // namespace caf::flow } // namespace caf::flow
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