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
...@@ -39,7 +39,7 @@ public: ...@@ -39,7 +39,7 @@ public:
using output_type = T; using output_type = T;
/// Internal interface of an `observable`. /// Internal interface of an `observable`.
class impl : public disposable::impl { class impl : public disposable_impl {
public: public:
// -- member types --------------------------------------------------------- // -- member types ---------------------------------------------------------
...@@ -49,29 +49,23 @@ public: ...@@ -49,29 +49,23 @@ public:
virtual coordinator* ctx() const noexcept = 0; virtual coordinator* ctx() const noexcept = 0;
// -- conversions ----------------------------------------------------------
observable as_observable() noexcept;
// -- flow processing ------------------------------------------------------ // -- flow processing ------------------------------------------------------
/// Subscribes a new observer. /// Subscribes a new observer.
virtual disposable subscribe(observer<T> what) = 0; virtual disposable subscribe(observer<T> what) = 0;
virtual void on_request(observer_impl<T>* sink, size_t n) = 0; // -- convenience functions ------------------------------------------------
virtual void on_cancel(observer_impl<T>* sink) = 0;
observable as_observable() noexcept;
/// Creates a subscription for `sink` and returns a @ref disposable to
/// cancel the observer.
disposable do_subscribe(observer_impl<T>* sink);
/// @copydoc do_subscribe
disposable do_subscribe(observer<T>& sink) {
return do_subscribe(sink.ptr());
}
/// Calls `on_error` on the `sink` with given `code` and returns a /// Calls `on_error` on the `sink` with given `code` and returns a
/// default-constructed @ref disposable. /// default-constructed @ref disposable.
disposable reject_subscription(observer_impl<T>* sink, sec code); disposable reject_subscription(observer_impl<T>* sink, sec code) {
sink->on_error(make_error(code));
return disposable{};
}
/// @copydoc reject_subscription /// @copydoc reject_subscription
disposable reject_subscription(observer<T>& sink, sec code) { disposable reject_subscription(observer<T>& sink, sec code) {
...@@ -79,58 +73,6 @@ public: ...@@ -79,58 +73,6 @@ public:
} }
}; };
class sub_impl final : public ref_counted, public subscription::impl {
public:
using src_type = typename observable<T>::impl;
using snk_type = typename observer<T>::impl;
CAF_INTRUSIVE_PTR_FRIENDS(sub_impl)
sub_impl(coordinator* ctx, src_type* src, snk_type* snk)
: ctx_(ctx), src_(src), snk_(snk) {
// nop
}
bool disposed() const noexcept override {
return src_ == nullptr;
}
void ref_disposable() const noexcept override {
this->ref();
}
void deref_disposable() const noexcept override {
this->deref();
}
void request(size_t n) override {
if (src_)
ctx()->delay_fn([src = src_, snk = snk_, n] { //
src->on_request(snk.get(), n);
});
}
void cancel() override {
if (src_) {
ctx()->delay_fn([src = src_, snk = snk_] { //
src->on_cancel(snk.get());
});
src_.reset();
snk_.reset();
}
}
auto* ctx() const noexcept {
return ctx_;
}
private:
coordinator* ctx_;
intrusive_ptr<src_type> src_;
intrusive_ptr<snk_type> snk_;
};
explicit observable(intrusive_ptr<impl> pimpl) noexcept explicit observable(intrusive_ptr<impl> pimpl) noexcept
: pimpl_(std::move(pimpl)) { : pimpl_(std::move(pimpl)) {
// nop // nop
...@@ -326,28 +268,15 @@ observable<T> observable<T>::impl::as_observable() noexcept { ...@@ -326,28 +268,15 @@ observable<T> observable<T>::impl::as_observable() noexcept {
return observable<T>{intrusive_ptr{this}}; return observable<T>{intrusive_ptr{this}};
} }
template <class T>
disposable observable<T>::impl::do_subscribe(observer_impl<T>* sink) {
sink->on_subscribe(subscription{make_counted<sub_impl>(ctx(), this, sink)});
// Note: we do NOT return the subscription here because this object is private
// to the observer. Outside code must call dispose() on the observer.
return disposable{intrusive_ptr<typename disposable::impl>{sink}};
}
template <class T>
disposable observable<T>::impl::reject_subscription(observer_impl<T>* sink, //
sec code) {
sink->on_error(make_error(code));
return disposable{};
}
/// @relates observable /// @relates observable
template <class T> template <class T>
using observable_impl = typename observable<T>::impl; using observable_impl = typename observable<T>::impl;
/// Default base type for observable implementation types. /// Default base type for observable implementation types.
template <class T> template <class T>
class observable_impl_base : public ref_counted, public observable_impl<T> { class observable_impl_base : public ref_counted,
public observable_impl<T>,
public subscription::listener {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
...@@ -361,7 +290,13 @@ public: ...@@ -361,7 +290,13 @@ public:
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
disposable as_disposable() noexcept {
// We need to implement this only for disambiguation since `as_disposable`
// exists in multiple base classes.
return subscription::listener::as_disposable();
}
void ref_disposable() const noexcept final { void ref_disposable() const noexcept final {
this->ref(); this->ref();
...@@ -371,12 +306,29 @@ public: ...@@ -371,12 +306,29 @@ public:
this->deref(); this->deref();
} }
// -- implementation of observable_impl<T> --------------------------------- // -- implementation of observable_impl<T> -----------------------------------
coordinator* ctx() const noexcept final { coordinator* ctx() const noexcept final {
return ctx_; return ctx_;
} }
// -- convenience functions --------------------------------------------------
/// Creates a subscription for `sink` and returns a @ref disposable to cancel
/// the observer.
disposable do_subscribe(observer_impl<T>* sink) {
sink->on_subscribe(subscription::default_impl::make(ctx_, this, sink));
// Note: we do NOT return the subscription here because this object is
// private to the observer. Outside code cancels flows by calling
// dispose on the observer.
return sink->as_disposable();
}
/// @copydoc do_subscribe
disposable do_subscribe(observer<T>& sink) {
return do_subscribe(sink.ptr());
}
protected: protected:
// -- member variables ------------------------------------------------------- // -- member variables -------------------------------------------------------
...@@ -538,18 +490,81 @@ private: ...@@ -538,18 +490,81 @@ private:
intrusive_ptr<impl> pimpl_; intrusive_ptr<impl> pimpl_;
}; };
/// @relates processor
template <class In, class Out = In> template <class In, class Out = In>
using processor_impl = typename processor<In, Out>::impl; using processor_impl = typename processor<In, Out>::impl;
// -- representing an error as an observable ----------------------------------- /// Default base type for processor implementation types.
/// @relates processor
template <class T> template <class In, class Out>
class observable_error_impl : public observable_impl_base<T> { class processor_impl_base : public ref_counted,
public processor_impl<In, Out>,
public subscription::listener {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using super = observable_impl_base<T>; using super = processor_impl<In, Out>;
using input_type = In;
using output_type = Out;
// -- constructors, destructors, and assignment operators --------------------
explicit processor_impl_base(coordinator* ctx) : ctx_(ctx) {
// nop
}
// -- implementation of disposable_impl --------------------------------------
disposable as_disposable() noexcept {
// We need to implement this only for disambiguation since `as_disposable`
// exists in multiple base classes.
return subscription::listener::as_disposable();
}
void ref_disposable() const noexcept final {
this->ref();
}
void deref_disposable() const noexcept final {
this->deref();
}
// -- implementation of processor_impl<T> -----------------------------------
coordinator* ctx() const noexcept final {
return ctx_;
}
// -- convenience functions --------------------------------------------------
/// Creates a subscription for `sink` and returns a @ref disposable to cancel
/// the observer.
disposable do_subscribe(observer_impl<Out>* sink) {
sink->on_subscribe(subscription::default_impl::make(ctx_, this, sink));
// Note: we do NOT return the subscription here because this object is
// private to the observer. Outside code cancels flows by calling
// dispose on the observer.
return disposable{intrusive_ptr<disposable_impl>{sink}};
}
/// @copydoc do_subscribe
disposable do_subscribe(observer<Out>& sink) {
return do_subscribe(sink.ptr());
}
protected:
// -- member variables -------------------------------------------------------
coordinator* ctx_;
};
// -- representing an error as an observable -----------------------------------
template <class T>
class observable_error_impl : public ref_counted, public observable_impl<T> {
public:
// -- friends ---------------------------------------------------------------- // -- friends ----------------------------------------------------------------
CAF_INTRUSIVE_PTR_FRIENDS(observable_error_impl) CAF_INTRUSIVE_PTR_FRIENDS(observable_error_impl)
...@@ -557,11 +572,19 @@ public: ...@@ -557,11 +572,19 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
observable_error_impl(coordinator* ctx, error what) observable_error_impl(coordinator* ctx, error what)
: super(ctx), what_(std::move(what)) { : ctx_(ctx), what_(std::move(what)) {
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void ref_disposable() const noexcept final {
this->ref();
}
void deref_disposable() const noexcept final {
this->deref();
}
void dispose() override { void dispose() override {
// nop // nop
...@@ -573,12 +596,8 @@ public: ...@@ -573,12 +596,8 @@ public:
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
void on_request(observer_impl<T>*, size_t) override { coordinator* ctx() const noexcept final {
CAF_RAISE_ERROR("observable_error_impl::on_request called"); return ctx_;
}
void on_cancel(observer_impl<T>*) override {
CAF_RAISE_ERROR("observable_error_impl::on_cancel called");
} }
disposable subscribe(observer<T> what) override { disposable subscribe(observer<T> what) override {
...@@ -596,9 +615,8 @@ private: ...@@ -596,9 +615,8 @@ private:
/// Broadcasts its input to all observers without modifying it. /// Broadcasts its input to all observers without modifying it.
template <class Step, class... Steps> template <class Step, class... Steps>
class broadcaster_impl class broadcaster_impl
: public ref_counted, : public processor_impl_base<typename Step::input_type,
public processor_impl<typename Step::input_type, steps_output_type_t<Step, Steps...>> {
steps_output_type_t<Step, Steps...>> {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
...@@ -606,6 +624,8 @@ public: ...@@ -606,6 +624,8 @@ public:
using output_type = steps_output_type_t<Step, Steps...>; using output_type = steps_output_type_t<Step, Steps...>;
using super = processor_impl_base<input_type, output_type>;
// -- friends ---------------------------------------------------------------- // -- friends ----------------------------------------------------------------
CAF_INTRUSIVE_PTR_FRIENDS(broadcaster_impl) CAF_INTRUSIVE_PTR_FRIENDS(broadcaster_impl)
...@@ -614,11 +634,11 @@ public: ...@@ -614,11 +634,11 @@ public:
template <class... Ts> template <class... Ts>
explicit broadcaster_impl(coordinator* ctx, Ts&&... step_args) explicit broadcaster_impl(coordinator* ctx, Ts&&... step_args)
: ctx_(ctx), steps_(std::forward<Ts>(step_args)...) { : super(ctx), steps_(std::forward<Ts>(step_args)...) {
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
...@@ -629,14 +649,6 @@ public: ...@@ -629,14 +649,6 @@ public:
return !term_.active(); return !term_.active();
} }
void ref_disposable() const noexcept override {
this->ref();
}
void deref_disposable() const noexcept override {
this->deref();
}
// -- implementation of observer<T>::impl ------------------------------------ // -- implementation of observer<T>::impl ------------------------------------
void on_subscribe(subscription sub) override { void on_subscribe(subscription sub) override {
...@@ -682,26 +694,24 @@ public: ...@@ -682,26 +694,24 @@ public:
} }
} }
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of subscription::listener -------------------------------
coordinator* ctx() const noexcept override {
return ctx_;
}
disposable subscribe(observer<output_type> sink) override {
return term_.add(this, sink);
}
void on_request(observer_impl<output_type>* sink, size_t n) override { void on_request(disposable_impl* sink, size_t n) override {
CAF_LOG_TRACE(CAF_ARG(n)); CAF_LOG_TRACE(CAF_ARG(n));
term_.on_request(sub_, sink, n); term_.on_request(sub_, sink, n);
} }
void on_cancel(observer_impl<output_type>* sink) override { void on_cancel(disposable_impl* sink) override {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
term_.on_cancel(sub_, sink); term_.on_cancel(sub_, sink);
} }
// -- implementation of observable<T>::impl ----------------------------------
disposable subscribe(observer<output_type> sink) override {
return term_.add(this, sink);
}
// -- properties ------------------------------------------------------------- // -- properties -------------------------------------------------------------
size_t buffered() const noexcept { size_t buffered() const noexcept {
...@@ -717,9 +727,6 @@ public: ...@@ -717,9 +727,6 @@ public:
} }
protected: protected:
/// Points to the coordinator that runs this observable.
coordinator* ctx_;
/// Allows us to request more items. /// Allows us to request more items.
subscription sub_; subscription sub_;
...@@ -957,7 +964,7 @@ public: ...@@ -957,7 +964,7 @@ public:
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
for (auto& kvp : inputs_) { for (auto& kvp : inputs_) {
...@@ -980,14 +987,14 @@ public: ...@@ -980,14 +987,14 @@ public:
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
void on_request(observer_impl<T>* sink, size_t demand) override { void on_request(disposable_impl* 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) {
pull(n); pull(n);
term_.push(); term_.push();
} }
} }
void on_cancel(observer_impl<T>* 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) {
pull(n); pull(n);
term_.push(); term_.push();
...@@ -1338,7 +1345,7 @@ public: ...@@ -1338,7 +1345,7 @@ public:
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
if (sub_) if (sub_)
...@@ -1351,9 +1358,9 @@ public: ...@@ -1351,9 +1358,9 @@ public:
return term_.finalized(); return term_.finalized();
} }
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of subscription::listener -------------------------------
void on_request(observer_impl<T>* sink, size_t demand) override { void on_request(disposable_impl* 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) {
in_flight_ += n; in_flight_ += n;
if (sub_) if (sub_)
...@@ -1361,7 +1368,7 @@ public: ...@@ -1361,7 +1368,7 @@ public:
} }
} }
void on_cancel(observer_impl<T>* 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) {
in_flight_ += n; in_flight_ += n;
if (sub_) if (sub_)
...@@ -1369,6 +1376,8 @@ public: ...@@ -1369,6 +1376,8 @@ public:
} }
} }
// -- implementation of observable<T>::impl ----------------------------------
disposable subscribe(observer<T> sink) override { disposable subscribe(observer<T> sink) override {
// On the first subscribe, we subscribe to our inputs unless the user did // On the first subscribe, we subscribe to our inputs unless the user did
// not add any inputs before that. In that case, we close immediately except // not add any inputs before that. In that case, we close immediately except
...@@ -1602,6 +1611,7 @@ auto observable<T>::concat_map(F f) { ...@@ -1602,6 +1611,7 @@ auto observable<T>::concat_map(F f) {
template <class T> template <class T>
class prefix_and_tail_observable_impl final class prefix_and_tail_observable_impl final
: public ref_counted, : public ref_counted,
public subscription::listener,
public observable_impl<T>, // For the forwarding to the 'tail'. public observable_impl<T>, // For the forwarding to the 'tail'.
public processor_impl<T, cow_tuple<std::vector<T>, observable<T>>> { public processor_impl<T, cow_tuple<std::vector<T>, observable<T>>> {
public: public:
...@@ -1626,7 +1636,7 @@ public: ...@@ -1626,7 +1636,7 @@ public:
CAF_INTRUSIVE_PTR_FRIENDS(prefix_and_tail_observable_impl) CAF_INTRUSIVE_PTR_FRIENDS(prefix_and_tail_observable_impl)
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
if (sub_) { if (sub_) {
...@@ -1647,62 +1657,70 @@ public: ...@@ -1647,62 +1657,70 @@ public:
return !sub_ && !obs_ && !tail_; return !sub_ && !obs_ && !tail_;
} }
void ref_disposable() const noexcept override { void ref_disposable() const noexcept final {
this->ref(); this->ref();
} }
void deref_disposable() const noexcept override { void deref_disposable() const noexcept final {
this->deref(); this->deref();
} }
// -- implementation of observable<in_t>::impl ------------------------------- // -- implementation of observable<T>::impl ----------------------------------
coordinator* ctx() const noexcept override { coordinator* ctx() const noexcept final {
return ctx_; return ctx_;
} }
// -- implementation of observable<in_t>::impl -------------------------------
disposable subscribe(observer<in_t> sink) override { disposable subscribe(observer<in_t> sink) override {
if (sink.ptr() == tail_.ptr()) { if (sink.ptr() == tail_.ptr()) {
return in_obs_t::do_subscribe(sink.ptr()); using sub_t = subscription::default_impl;
sink.on_subscribe(sub_t::make_unsafe(ctx_, this, sink.ptr()));
return sink.as_disposable();
} else { } else {
sink.on_error(make_error(sec::invalid_observable)); sink.on_error(make_error(sec::invalid_observable));
return disposable{}; return disposable{};
} }
} }
void on_request(observer_impl<in_t>*, size_t n) override {
if (sub_)
sub_.request(n);
}
void on_cancel(observer_impl<in_t>*) override {
if (sub_) {
sub_.cancel();
sub_ = nullptr;
}
}
// -- implementation of observable<out_t>::impl ------------------------------ // -- implementation of observable<out_t>::impl ------------------------------
disposable subscribe(observer<out_t> sink) override { disposable subscribe(observer<out_t> sink) override {
obs_ = sink; obs_ = sink;
return out_obs_t::do_subscribe(sink.ptr()); using sub_t = subscription::default_impl;
sink.on_subscribe(sub_t::make_unsafe(ctx_, this, sink.ptr()));
return sink.as_disposable();
} }
void on_request(observer_impl<out_t>*, size_t) override { // -- implementation of subscription::listener -------------------------------
if (sub_ && !requested_prefix_) {
requested_prefix_ = true; void on_request(disposable_impl* sink, size_t n) override {
sub_.request(prefix_size_); if (sink == obs_.ptr()) {
if (sub_ && !requested_prefix_) {
requested_prefix_ = true;
sub_.request(prefix_size_);
}
} else if (sink == tail_.ptr()) {
if (sub_)
sub_.request(n);
} }
} }
void on_cancel(observer_impl<out_t>*) override { void on_cancel(disposable_impl* sink) override {
// Only has an effect when canceling immediately. Otherwise, we forward to if (sink == obs_.ptr()) {
// tail_ and the original observer no longer is of any interest since it // Only has an effect when canceling immediately. Otherwise, we forward to
// receives at most one item anyways. // tail_ and the original observer no longer is of any interest since it
if (sub_ && !tail_) { // receives at most one item anyways.
sub_.cancel(); if (sub_ && !tail_) {
sub_ = nullptr; sub_.cancel();
sub_ = nullptr;
}
} else if (sink == tail_.ptr()) {
if (sub_) {
sub_.cancel();
sub_ = nullptr;
}
} }
} }
...@@ -1845,7 +1863,7 @@ public: ...@@ -1845,7 +1863,7 @@ public:
this->ctx()->deref_coordinator(); this->ctx()->deref_coordinator();
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
...@@ -1865,14 +1883,14 @@ public: ...@@ -1865,14 +1883,14 @@ public:
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
void on_request(observer_impl<value_type>*, size_t n) override { void on_request(disposable_impl*, size_t n) override {
CAF_LOG_TRACE(CAF_ARG(n)); CAF_LOG_TRACE(CAF_ARG(n));
demand_ += n; demand_ += n;
if (demand_ == n) if (demand_ == n)
pull(); pull();
} }
void on_cancel(observer_impl<value_type>*) override { void on_cancel(disposable_impl*) override {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
dst_ = nullptr; dst_ = nullptr;
dispose(); dispose();
...@@ -2033,7 +2051,7 @@ public: ...@@ -2033,7 +2051,7 @@ public:
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
// nop // nop
...@@ -2045,11 +2063,11 @@ public: ...@@ -2045,11 +2063,11 @@ public:
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
void on_request(observer_impl<output_type>* snk, size_t) override { void on_request(disposable_impl* snk, size_t) override {
snk->on_complete(); static_cast<observer_impl<output_type>*>(snk)->on_complete();
} }
void on_cancel(observer_impl<output_type>*) override { void on_cancel(disposable_impl*) override {
// nop // nop
} }
...@@ -2081,7 +2099,7 @@ public: ...@@ -2081,7 +2099,7 @@ public:
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
if (!disposed_) { if (!disposed_) {
...@@ -2097,11 +2115,11 @@ public: ...@@ -2097,11 +2115,11 @@ public:
// -- implementation of observable<T>::impl ---------------------------------- // -- implementation of observable<T>::impl ----------------------------------
void on_request(observer_impl<output_type>*, size_t) override { void on_request(disposable_impl*, size_t) override {
// nop // nop
} }
void on_cancel(observer_impl<output_type>*) override { void on_cancel(disposable_impl*) override {
// nop // nop
} }
...@@ -2141,7 +2159,7 @@ public: ...@@ -2141,7 +2159,7 @@ public:
// nop // nop
} }
// -- implementation of disposable::impl ------------------------------------- // -- implementation of disposable_impl --------------------------------------
void dispose() override { void dispose() override {
if (is_active(state)) { if (is_active(state)) {
...@@ -2160,13 +2178,13 @@ public: ...@@ -2160,13 +2178,13 @@ public:
// -- implementation of observable_impl<T> ----------------------------------- // -- implementation of observable_impl<T> -----------------------------------
void on_request(observer_impl<output_type>* sink, size_t n) override { void on_request(disposable_impl* sink, size_t n) override {
if (out.ptr() == sink) { if (out.ptr() == sink) {
demand += n; demand += n;
} }
} }
void on_cancel(observer_impl<output_type>* sink) override { void on_cancel(disposable_impl* sink) override {
if (out.ptr() == sink) { if (out.ptr() == sink) {
demand = 0; demand = 0;
out = nullptr; out = nullptr;
......
...@@ -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