Commit 19d7960b authored by Kirk Shoop's avatar Kirk Shoop Committed by Facebook Github Bot

change definition of Executor

Summary: convert all executors in pushmi and all executor concepts and usage in pushmi to use `schedule()`

Reviewed By: ericniebler

Differential Revision: D13463902

fbshipit-source-id: 01eac4fe0b5cd49afbd95118db769db59a3b968c
parent ce0fd826
......@@ -56,12 +56,6 @@ struct construct_deduced<many_sender>;
template<>
struct construct_deduced<flow_single_sender>;
template<>
struct construct_deduced<constrained_single_sender>;
template<>
struct construct_deduced<time_single_sender>;
template<>
struct construct_deduced<flow_many_sender>;
......@@ -115,7 +109,6 @@ struct priorityZeroF {
struct passDVF {
PUSHMI_TEMPLATE(class Data, class... VN)
// (requires True<>) //
(requires requires (
set_value(std::declval<Data&>(), std::declval<VN>()...)
) && Receiver<Data>)
......@@ -127,8 +120,8 @@ struct passDVF {
struct passDEF {
PUSHMI_TEMPLATE(class E, class Data)
(requires ReceiveError<Data, E>)
void operator()(Data& out, E e) const noexcept {
set_error(out, e);
void operator()(Data& out, E&& e) const noexcept {
set_error(out, (E&&)e);
}
};
......@@ -154,7 +147,7 @@ struct passDEXF {
PUSHMI_TEMPLATE(class Data)
(requires Sender<Data>)
auto operator()(Data& in) const noexcept {
return executor(in);
return get_executor(in);
}
};
......@@ -171,7 +164,7 @@ struct passDSF {
struct passDNF {
PUSHMI_TEMPLATE(class Data)
(requires TimeSender<Data>)
(requires TimeExecutor<Data>)
auto operator()(Data& in) const noexcept {
return ::folly::pushmi::now(in);
}
......@@ -179,7 +172,7 @@ struct passDNF {
struct passDZF {
PUSHMI_TEMPLATE(class Data)
(requires ConstrainedSender<Data>)
(requires ConstrainedExecutor<Data>)
auto operator()(Data& in) const noexcept {
return ::folly::pushmi::top(in);
}
......@@ -309,6 +302,17 @@ auto on_executor(Fn fn) -> on_executor_fn<Fn> {
return on_executor_fn<Fn>{std::move(fn)};
}
template <class Fn>
struct on_make_strand_fn : overload_fn<Fn> {
constexpr on_make_strand_fn() = default;
using overload_fn<Fn>::overload_fn;
};
template <class Fn>
auto on_make_strand(Fn fn) -> on_make_strand_fn<Fn> {
return on_make_strand_fn<Fn>{std::move(fn)};
}
template <class... Fns>
struct on_submit_fn : overload_fn<Fns...> {
constexpr on_submit_fn() = default;
......@@ -320,6 +324,17 @@ auto on_submit(Fns... fns) -> on_submit_fn<Fns...> {
return on_submit_fn<Fns...>{std::move(fns)...};
}
template <class... Fns>
struct on_schedule_fn : overload_fn<Fns...> {
constexpr on_schedule_fn() = default;
using overload_fn<Fns...>::overload_fn;
};
template <class... Fns>
auto on_schedule(Fns... fns) -> on_schedule_fn<Fns...> {
return on_schedule_fn<Fns...>{std::move(fns)...};
}
template <class Fn>
struct on_now_fn : overload_fn<Fn> {
constexpr on_now_fn() = default;
......
......@@ -48,13 +48,12 @@ struct receiver_category {};
struct sender_category {};
// for senders that are executors
// for executors
// time and constrained are mutually exclusive refinements of executor (time is
// a special case of constrained and may be folded in later)
struct executor_category {};
// time and constrained are mutually exclusive refinements of sender (time is a
// special case of constrained and may be folded in later)
// blocking affects senders
struct blocking_category {};
......@@ -130,11 +129,6 @@ template <class PS>
struct is_receiver<PS> : property_query<PS, is_receiver<>> {};
template <class PS>
PUSHMI_INLINE_VAR constexpr bool is_receiver_v = is_receiver<PS>::value;
// PUSHMI_CONCEPT_DEF(
// template (class PS)
// concept Receiver,
// is_receiver_v<PS>
// );
// Sender trait and tag
template <class... TN>
......@@ -149,11 +143,6 @@ template <class PS>
struct is_sender<PS> : property_query<PS, is_sender<>> {};
template <class PS>
PUSHMI_INLINE_VAR constexpr bool is_sender_v = is_sender<PS>::value;
// PUSHMI_CONCEPT_DEF(
// template (class PS)
// concept Sender,
// is_sender_v<PS>
// );
// Executor trait and tag
template <class... TN>
......@@ -168,17 +157,13 @@ template <class PS>
struct is_executor<PS> : property_query<PS, is_executor<>> {};
template <class PS>
PUSHMI_INLINE_VAR constexpr bool is_executor_v = is_executor<PS>::value;
PUSHMI_CONCEPT_DEF(
template(class PS) //
concept Executor, //
is_executor_v<PS>&& is_sender_v<PS>&& is_single_v<PS>);
// Constrained trait and tag
template <class... TN>
struct is_constrained;
// Tag
template <>
struct is_constrained<> : is_sender<> {};
struct is_constrained<> : is_executor<> {};
// Trait
template <class PS>
struct is_constrained<PS> : property_query<PS, is_constrained<>> {};
......@@ -187,7 +172,7 @@ PUSHMI_INLINE_VAR constexpr bool is_constrained_v = is_constrained<PS>::value;
PUSHMI_CONCEPT_DEF(
template(class PS) //
concept Constrained, //
is_constrained_v<PS>&& is_sender_v<PS>);
is_constrained_v<PS>&& is_executor_v<PS>);
// Time trait and tag
template <class... TN>
......@@ -203,7 +188,7 @@ PUSHMI_INLINE_VAR constexpr bool is_time_v = is_time<PS>::value;
PUSHMI_CONCEPT_DEF(
template(class PS) //
concept Time, //
is_time_v<PS>&& is_constrained_v<PS>&& is_sender_v<PS>);
is_time_v<PS>&& is_constrained_v<PS>&& is_executor_v<PS>);
// AlwaysBlocking trait and tag
template <class... TN>
......@@ -279,7 +264,7 @@ PUSHMI_INLINE_VAR constexpr bool is_fifo_sequence_v =
PUSHMI_CONCEPT_DEF(
template(class PS) //
concept FifoSequence, //
is_fifo_sequence_v<PS>&& is_sender_v<PS>);
is_fifo_sequence_v<PS>&& is_executor_v<PS>);
// ConcurrentSequence trait and tag
template <class... TN>
......@@ -299,64 +284,153 @@ PUSHMI_INLINE_VAR constexpr bool is_concurrent_sequence_v =
PUSHMI_CONCEPT_DEF(
template(class PS) //
concept ConcurrentSequence, //
is_concurrent_sequence_v<PS>&& is_sender_v<PS>);
is_concurrent_sequence_v<PS>&& is_executor_v<PS>);
// concepts to support execution
//
PUSHMI_CONCEPT_DEF(
template(class Exec, class... PropertyN) //
(concept Executor)(Exec, PropertyN...), //
requires(Exec& exec) //
(schedule(exec), requires_<is_sender_v<decltype(schedule(exec))>>) &&
SemiMovable<std::decay_t<Exec>> && property_query_v<Exec, PropertyN...> &&
is_executor_v<Exec>);
template <class Exec>
PUSHMI_PP_CONSTRAINED_USING(
Executor<Exec>,
sender_t =,
decltype(schedule(std::declval<Exec&>())));
PUSHMI_CONCEPT_DEF(
template(class Exec, class... PropertyN) //
(concept ExecutorProvider)(Exec, PropertyN...), //
requires(Exec& exec) //
(get_executor(exec),
requires_<Executor<decltype(get_executor(exec))>>) &&
SemiMovable<std::decay_t<Exec>> && property_query_v<Exec, PropertyN...>);
PUSHMI_CONCEPT_DEF(
template(class Exec, class... PropertyN) //
(concept Strand)(Exec, PropertyN...), //
Executor<Exec>&& property_query_v<Exec, is_fifo_sequence<>, PropertyN...>);
PUSHMI_CONCEPT_DEF(
template(class Exec) //
(concept StrandFactory)(Exec), //
requires(Exec& exec) //
(make_strand(exec), requires_<Strand<decltype(make_strand(exec))>>) &&
SemiMovable<std::decay_t<Exec>>);
template <class Exec>
PUSHMI_PP_CONSTRAINED_USING(
StrandFactory<Exec>,
strand_t =,
decltype(make_strand(std::declval<Exec&>())));
// add concepts for execution constraints
//
// the constraint could be time or priority enum or any other
// ordering constraint value-type.
//
// top() returns the constraint value that will cause the item to run asap.
// So now() for time and NORMAL for priority.
//
PUSHMI_CONCEPT_DEF(
template(class Exec, class... PropertyN) //
(concept ConstrainedExecutor)(Exec, PropertyN...), //
requires(Exec& exec) //
(top(exec),
requires_<Regular<decltype(top(exec))>>,
schedule(exec, top(exec)),
requires_<is_sender_v<decltype(schedule(exec, top(exec)))>>) &&
Executor<Exec, is_constrained<>, PropertyN...>);
template <class Exec>
PUSHMI_PP_CONSTRAINED_USING(
ConstrainedExecutor<Exec>,
constraint_t =,
decltype(top(std::declval<Exec&>())));
PUSHMI_CONCEPT_DEF(
template(class Exec, class... PropertyN) //
(concept TimeExecutor)(Exec, PropertyN...), //
requires(Exec& exec) //
(now(exec),
schedule(exec, now(exec)),
requires_<Regular<decltype(now(exec) + std::chrono::seconds(1))>>) &&
ConstrainedExecutor<Exec, is_time<>, PropertyN...>);
template <class Exec>
PUSHMI_PP_CONSTRAINED_USING(
TimeExecutor<Exec>,
time_point_t =,
decltype(now(std::declval<Exec&>())));
// add concepts to support receivers
//
PUSHMI_CONCEPT_DEF(
template(class R, class... PropertyN) //
(concept Receiver)(R, PropertyN...), //
requires(R& r)( //
set_done(r),
set_error(r, std::exception_ptr{})) &&
SemiMovable<R> && property_query_v<R, PropertyN...> &&
is_receiver_v<R> && !is_sender_v<R>);
requires(R& r) //
(set_done(r)) &&
SemiMovable<std::decay_t<R>> &&
property_query_v<R, PropertyN...> &&
is_receiver_v<R> && !is_sender_v<R>
);
PUSHMI_CONCEPT_DEF(
template(class R, class... VN) //
(concept ReceiveValue)(R, VN...), //
requires(R& r)( //
set_value(r, std::declval<VN&&>()...)) &&
Receiver<R> &&
// GCC w/-fconcepts ICE on SemiMovable<VN>...
True<> // And<SemiMovable<VN>...>
);
requires(R& r) //
(set_value(r, std::declval<VN&&>()...)) &&
Receiver<R>&& And<MoveConstructible<VN>...>);
PUSHMI_CONCEPT_DEF(
template(class R, class E = std::exception_ptr) //
(concept ReceiveError)(R, E), //
requires(R& r, E&& e)( //
set_error(r, (E &&) e)) &&
Receiver<R> && SemiMovable<E>
);
Receiver<R> && MoveConstructible<E>);
// add concepts to support senders
//
PUSHMI_CONCEPT_DEF(
template(class D, class... PropertyN) //
(concept Sender)(D, PropertyN...), //
requires(D& d)( //
executor(d),
requires_<Executor<decltype(executor(d))>>) &&
SemiMovable<D> && Cardinality<D> && property_query_v<D, PropertyN...> &&
is_sender_v<D> && !is_receiver_v<D>);
template(class S, class... PropertyN) //
(concept SenderImpl)(S, PropertyN...), //
SemiMovable<S>&& Cardinality<S>&& property_query_v<S, PropertyN...>&&
is_sender_v<S> &&
!is_receiver_v<S>);
PUSHMI_CONCEPT_DEF(
template(class S, class... PropertyN) //
(concept Sender)(S, PropertyN...), //
SenderImpl<std::decay_t<S>, PropertyN...>);
PUSHMI_CONCEPT_DEF(
template(class D, class S, class... PropertyN) //
(concept SenderTo)(D, S, PropertyN...), //
requires(D& d, S&& s)( //
submit(d, (S &&) s)) &&
Sender<D> && Receiver<S> && property_query_v<D, PropertyN...>);
template(class S, class R, class... PropertyN) //
(concept SenderTo)(S, R, PropertyN...), //
requires(S&& s, R&& r) //
(submit((S &&) s, (R &&) r)) &&
Sender<S, PropertyN...> && Receiver<R>);
template <class D>
template <class S>
PUSHMI_PP_CONSTRAINED_USING(
Sender<D>,
Sender<S>,
executor_t =,
decltype(executor(std::declval<D&>())));
decltype(get_executor(std::declval<S&>())));
// add concepts to support cancellation
// add concepts to support cancellation and rate control
//
PUSHMI_CONCEPT_DEF(
template(class S, class... PropertyN) //
(concept FlowReceiver)(S, PropertyN...), //
Receiver<S>&& property_query_v<S, PropertyN...>&& Flow<S>);
template(class R, class... PropertyN) //
(concept FlowReceiver)(R, PropertyN...), //
Receiver<R>&& property_query_v<R, PropertyN...>&& Flow<R>);
PUSHMI_CONCEPT_DEF(
template(class R, class... VN) //
......@@ -371,8 +445,8 @@ PUSHMI_CONCEPT_DEF(
PUSHMI_CONCEPT_DEF(
template(class R, class Up) //
(concept FlowUpTo)(R, Up), //
requires(R& r, Up&& up)( //
set_starting(r, (Up &&) up)) &&
requires(R& r, Up&& up) //
(set_starting(r, (Up &&) up)) &&
Flow<R>);
PUSHMI_CONCEPT_DEF(
......@@ -381,59 +455,10 @@ PUSHMI_CONCEPT_DEF(
Sender<S>&& property_query_v<S, PropertyN...>&& Flow<S>);
PUSHMI_CONCEPT_DEF(
template(class D, class S, class... PropertyN) //
(concept FlowSenderTo)(D, S, PropertyN...), //
FlowSender<D>&& property_query_v<D, PropertyN...>&& FlowReceiver<S>);
// add concepts for constraints
//
// the constraint could be time or priority enum or any other
// ordering constraint value-type.
//
// top() returns the constraint value that will cause the item to run asap.
// So now() for time and NORMAL for priority.
//
PUSHMI_CONCEPT_DEF(
template(class D, class... PropertyN) //
(concept ConstrainedSender)(D, PropertyN...), //
requires(D& d)( //
top(d),
requires_<Regular<decltype(top(d))>>) &&
Sender<D> && property_query_v<D, PropertyN...> && Constrained<D>);
PUSHMI_CONCEPT_DEF(
template(class D, class S, class... PropertyN) //
(concept ConstrainedSenderTo)(D, S, PropertyN...), //
requires(D& d, S&& s)( //
submit(d, top(d), (S &&) s)) &&
ConstrainedSender<D> && property_query_v<D, PropertyN...> &&
Receiver<S>);
template <class D>
PUSHMI_PP_CONSTRAINED_USING(
ConstrainedSender<D>,
constraint_t =,
decltype(top(std::declval<D&>())));
PUSHMI_CONCEPT_DEF(
template(class D, class... PropertyN) //
(concept TimeSender)(D, PropertyN...), //
requires(D& d)( //
now(d),
requires_<Regular<decltype(now(d) + std::chrono::seconds(1))>>) &&
ConstrainedSender<D, PropertyN...> && Time<D>);
PUSHMI_CONCEPT_DEF(
template(class D, class S, class... PropertyN) //
(concept TimeSenderTo)(D, S, PropertyN...), //
ConstrainedSenderTo<D, S, PropertyN...>&& TimeSender<D>);
template <class D>
PUSHMI_PP_CONSTRAINED_USING(
TimeSender<D>,
time_point_t =,
decltype(now(std::declval<D&>())));
template(class S, class R, class... PropertyN) //
(concept FlowSenderTo)(S, R, PropertyN...), //
FlowSender<S>&& SenderTo<S, R>&& property_query_v<S, PropertyN...>&&
FlowReceiver<R>);
} // namespace pushmi
} // namespace folly
/*
* Copyright 2018-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/experimental/pushmi/receiver.h>
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/inline.h>
namespace folly {
namespace pushmi {
template <class E, class CV, class... VN>
class any_constrained_single_sender {
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::promise<int>)]; // can hold a V in-situ
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible<Wrapped>::value;
}
struct vtable {
static void s_op(data&, data*) {}
static CV s_top(data&) { return CV{}; }
static any_constrained_executor<E, CV> s_executor(data&) { return {}; }
static void s_submit(data&, CV, any_receiver<E, VN...>) {}
void (*op_)(data&, data*) = vtable::s_op;
CV (*top_)(data&) = vtable::s_top;
any_constrained_executor<E, CV> (*executor_)(data&) = vtable::s_executor;
void (*submit_)(data&, CV, any_receiver<E, VN...>) = vtable::s_submit;
};
static constexpr vtable const noop_ {};
vtable const* vptr_ = &noop_;
template <class Wrapped>
any_constrained_single_sender(Wrapped obj, std::false_type)
: any_constrained_single_sender() {
struct s {
static void op(data& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static CV top(data& src) {
return ::folly::pushmi::top(*static_cast<Wrapped*>(src.pobj_));
}
static any_constrained_executor<E, CV> executor(data& src) {
return any_constrained_executor<E, CV>{
executor(*static_cast<Wrapped*>(src.pobj_))};
}
static void submit(data& src, CV at, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>(src.pobj_), std::move(at), std::move(out));
}
};
static const vtable vtbl{s::op, s::top, s::executor, s::submit};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_constrained_single_sender(Wrapped obj, std::true_type) noexcept
: any_constrained_single_sender() {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_) Wrapped(
std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static CV top(data& src) {
return ::folly::pushmi::top(*static_cast<Wrapped*>((void*)src.buffer_));
}
static any_constrained_executor<E, CV> executor(data& src) {
return any_constrained_executor<E, CV>{::folly::pushmi::executor(
*static_cast<Wrapped*>((void*)src.buffer_))};
}
static void submit(data& src, CV cv, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>((void*)src.buffer_),
std::move(cv),
std::move(out));
}
};
static const vtable vtbl{s::op, s::top, s::executor, s::submit};
new (data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
using wrapped_t =
std::enable_if_t<!std::is_same<U, any_constrained_single_sender>::value, U>;
public:
using properties = property_set<is_constrained<>, is_single<>>;
any_constrained_single_sender() = default;
any_constrained_single_sender(any_constrained_single_sender&& that) noexcept
: any_constrained_single_sender() {
that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_);
}
PUSHMI_TEMPLATE (class Wrapped)
(requires ConstrainedSenderTo<wrapped_t<Wrapped>, any_receiver<E, VN...>>)
explicit any_constrained_single_sender(Wrapped obj) noexcept(insitu<Wrapped>())
: any_constrained_single_sender{std::move(obj), bool_<insitu<Wrapped>()>{}} {
}
~any_constrained_single_sender() {
vptr_->op_(data_, nullptr);
}
any_constrained_single_sender& operator=(any_constrained_single_sender&& that) noexcept {
this->~any_constrained_single_sender();
new ((void*)this) any_constrained_single_sender(std::move(that));
return *this;
}
CV top() {
return vptr_->top_(data_);
}
any_constrained_executor<E, CV> executor() {
return vptr_->executor_(data_);
}
void submit(CV at, any_receiver<E, VN...> out) {
vptr_->submit_(data_, std::move(at), std::move(out));
}
};
// Class static definitions:
template <class E, class CV, class... VN>
constexpr typename any_constrained_single_sender<E, CV, VN...>::vtable const
any_constrained_single_sender<E, CV, VN...>::noop_;
template<class SF, class ZF, class EXF>
// (requires Invocable<ZF&> && Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
class constrained_single_sender<SF, ZF, EXF> {
SF sf_;
ZF zf_;
EXF exf_;
public:
using properties = property_set<is_constrained<>, is_single<>>;
constexpr constrained_single_sender() = default;
constexpr explicit constrained_single_sender(SF sf)
: sf_(std::move(sf)) {}
constexpr constrained_single_sender(SF sf, EXF exf)
: sf_(std::move(sf)), exf_(std::move(exf)) {}
constexpr constrained_single_sender(SF sf, EXF exf, ZF zf)
: sf_(std::move(sf)), zf_(std::move(zf)), exf_(std::move(exf)) {}
auto top() {
return zf_();
}
auto executor() { return exf_(); }
PUSHMI_TEMPLATE(class CV, class Out)
(requires Regular<CV> && Receiver<Out> &&
Invocable<SF&, CV, Out>)
void submit(CV cv, Out out) {
sf_(std::move(cv), std::move(out));
}
};
template <PUSHMI_TYPE_CONSTRAINT(ConstrainedSender<is_single<>>) Data, class DSF, class DZF, class DEXF>
#if __cpp_concepts
requires Invocable<DZF&, Data&> && Invocable<DEXF&, Data&>
#endif
class constrained_single_sender<Data, DSF, DZF, DEXF> {
Data data_;
DSF sf_;
DZF zf_;
DEXF exf_;
public:
using properties = property_set_insert_t<properties_t<Data>, property_set<is_single<>>>;
constexpr constrained_single_sender() = default;
constexpr explicit constrained_single_sender(Data data)
: data_(std::move(data)) {}
constexpr constrained_single_sender(Data data, DSF sf, DEXF exf = DEXF{})
: data_(std::move(data)), sf_(std::move(sf)), exf_(std::move(exf)) {}
constexpr constrained_single_sender(Data data, DSF sf, DEXF exf, DZF zf)
: data_(std::move(data)), sf_(std::move(sf)), zf_(std::move(zf)), exf_(std::move(exf)) {}
auto top() {
return zf_(data_);
}
auto executor() { return exf_(data_); }
PUSHMI_TEMPLATE(class CV, class Out)
(requires Regular<CV> && Receiver<Out> &&
Invocable<DSF&, Data&, CV, Out>)
void submit(CV cv, Out out) {
sf_(data_, std::move(cv), std::move(out));
}
};
template <>
class constrained_single_sender<>
: public constrained_single_sender<ignoreSF, priorityZeroF, inlineConstrainedEXF> {
public:
constrained_single_sender() = default;
};
////////////////////////////////////////////////////////////////////////////////
// make_constrained_single_sender
PUSHMI_INLINE_VAR constexpr struct make_constrained_single_sender_fn {
inline auto operator()() const {
return constrained_single_sender<ignoreSF, priorityZeroF, inlineConstrainedEXF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf) const {
return constrained_single_sender<SF, priorityZeroF, inlineConstrainedEXF>{std::move(sf)};
}
PUSHMI_TEMPLATE (class SF, class EXF)
(requires Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf, EXF exf) const {
return constrained_single_sender<SF, priorityZeroF, EXF>{std::move(sf), std::move(exf)};
}
PUSHMI_TEMPLATE (class SF, class ZF, class EXF)
(requires Invocable<ZF&> && Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf, EXF exf, ZF zf) const {
return constrained_single_sender<SF, ZF, EXF>{std::move(sf), std::move(exf), std::move(zf)};
}
PUSHMI_TEMPLATE (class Data)
(requires ConstrainedSender<Data, is_single<>>)
auto operator()(Data d) const {
return constrained_single_sender<Data, passDSF, passDZF, passDEXF>{std::move(d)};
}
PUSHMI_TEMPLATE (class Data, class DSF)
(requires ConstrainedSender<Data, is_single<>>)
auto operator()(Data d, DSF sf) const {
return constrained_single_sender<Data, DSF, passDZF, passDEXF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE (class Data, class DSF, class DEXF)
(requires ConstrainedSender<Data, is_single<>> && Invocable<DEXF&, Data&>)
auto operator()(Data d, DSF sf, DEXF exf) const {
return constrained_single_sender<Data, DSF, passDZF, DEXF>{std::move(d), std::move(sf),
std::move(exf)};
}
PUSHMI_TEMPLATE (class Data, class DSF, class DZF, class DEXF)
(requires ConstrainedSender<Data, is_single<>> && Invocable<DZF&, Data&> && Invocable<DEXF&, Data&>)
auto operator()(Data d, DSF sf, DEXF exf, DZF zf) const {
return constrained_single_sender<Data, DSF, DZF, DEXF>{std::move(d), std::move(sf),
std::move(exf), std::move(zf)};
}
} const make_constrained_single_sender {};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
constrained_single_sender() -> constrained_single_sender<ignoreSF, priorityZeroF, inlineConstrainedEXF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
constrained_single_sender(SF) -> constrained_single_sender<SF, priorityZeroF, inlineConstrainedEXF>;
PUSHMI_TEMPLATE (class SF, class EXF)
(requires Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
constrained_single_sender(SF, EXF) -> constrained_single_sender<SF, priorityZeroF, EXF>;
PUSHMI_TEMPLATE (class SF, class ZF, class EXF)
(requires Invocable<ZF&> && Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
constrained_single_sender(SF, EXF, ZF) -> constrained_single_sender<SF, ZF, EXF>;
PUSHMI_TEMPLATE (class Data, class DSF)
(requires ConstrainedSender<Data, is_single<>>)
constrained_single_sender(Data, DSF) -> constrained_single_sender<Data, DSF, passDZF, passDEXF>;
PUSHMI_TEMPLATE (class Data, class DSF, class DEXF)
(requires ConstrainedSender<Data, is_single<>> && Invocable<DEXF&, Data&>)
constrained_single_sender(Data, DSF, DEXF) -> constrained_single_sender<Data, DSF, passDZF, DEXF>;
PUSHMI_TEMPLATE (class Data, class DSF, class DZF, class DEXF)
(requires ConstrainedSender<Data, is_single<>> && Invocable<DZF&, Data&> && Invocable<DEXF&, Data&>)
constrained_single_sender(Data, DSF, DEXF, DZF) -> constrained_single_sender<Data, DSF, DZF, DEXF>;
#endif
template<>
struct construct_deduced<constrained_single_sender>
: make_constrained_single_sender_fn {};
} // namespace pushmi
} // namespace folly
......@@ -23,7 +23,7 @@
// disable buggy compatibility warning about "requires" and "concept" being
// C++20 keywords.
#if defined(__clang__) && not defined(__APPLE__)
#if defined(__clang__) && !defined(__APPLE__)
#define PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") \
......@@ -419,9 +419,7 @@ PUSHMI_PP_IGNORE_CXX2A_COMPAT_BEGIN
#else
#define PUSHMI_BROKEN_SUBSUMPTION(...) __VA_ARGS__
#define PUSHMI_TYPE_CONSTRAINT(...) class
// bool() is used to prevent 'error: pasting "PUSHMI_PP_REQUIRES_PROBE_" and
// "::" does not give a valid preprocessing token'
#define PUSHMI_EXP(...) bool(::folly::pushmi::expAnd(__VA_ARGS__))
#define PUSHMI_EXP(...) decltype(::folly::pushmi::expAnd(__VA_ARGS__)){}
#define PUSHMI_AND ,
#endif
......@@ -484,7 +482,7 @@ struct Not {
template <class T, class U>
struct And {
explicit constexpr operator bool() const noexcept {
return (bool)std::conditional_t<(bool)T{}, U, std::false_type>{};
return static_cast<bool>(std::conditional_t<static_cast<bool>(T{}), U, std::false_type>{});
}
PUSHMI_TEMPLATE(class This = And, bool B) //
(requires B == static_cast<bool>(This{})) //
......@@ -507,7 +505,7 @@ struct And {
template <class T, class U>
struct Or {
explicit constexpr operator bool() const noexcept {
return (bool)std::conditional_t<(bool)T{}, std::true_type, U>{};
return static_cast<bool>(std::conditional_t<static_cast<bool>(T{}), std::true_type, U>{});
}
PUSHMI_TEMPLATE(class This = Or, bool B) //
(requires B == static_cast<bool>(This{})) //
......
......@@ -28,16 +28,20 @@ PUSHMI_INLINE_VAR constexpr struct invoke_fn {
using mem_fn_t = decltype(std::mem_fn(std::declval<F>()));
public:
template <class F, class... As>
PUSHMI_TEMPLATE(class F, class... As)
(requires //
requires(
std::declval<F>()(std::declval<As>()...))) //
auto operator()(F&& f, As&&... as) const
noexcept(noexcept(((F &&) f)((As &&) as...)))
-> decltype(((F &&) f)((As &&) as...)) {
noexcept(noexcept(((F &&) f)((As &&) as...))) {
return ((F &&) f)((As &&) as...);
}
template <class F, class... As>
PUSHMI_TEMPLATE(class F, class... As)
(requires //
requires(
std::mem_fn(std::declval<F>())(std::declval<As>()...))) //
auto operator()(F&& f, As&&... as) const
noexcept(noexcept(std::declval<mem_fn_t<F>>()((As &&) as...)))
-> decltype(std::mem_fn(f)((As &&) as...)) {
noexcept(noexcept(std::declval<mem_fn_t<F>>()((As &&) as...))) {
return std::mem_fn(f)((As &&) as...);
}
} invoke{};
......
......@@ -32,10 +32,11 @@ struct no_fail_fn {
};
template <class In>
struct out_impl {
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)auto operator()(Out out) const {
return ::folly::pushmi::detail::receiver_from_fn<In>()(
std::move(out), ::folly::pushmi::on_error(on_error_impl{}));
PUSHMI_TEMPLATE(class SIn, class Out)
(requires Receiver<Out>) //
void operator()(SIn&& in, Out out) const {
submit((In&&)in, ::folly::pushmi::detail::receiver_from_fn<In>()(
std::move(out), ::folly::pushmi::on_error(on_error_impl{})));
}
};
struct in_impl {
......@@ -43,7 +44,7 @@ struct no_fail_fn {
(requires Sender<In>)auto operator()(In in) const {
return ::folly::pushmi::detail::sender_from(
std::move(in),
::folly::pushmi::detail::submit_transform_out<In>(out_impl<In>{}));
out_impl<In>{});
}
};
......
......@@ -33,7 +33,7 @@ auto get_setting() {
if (setting_exists) {
op::just(42) | op::submit(out);
} else {
op::empty<int>() | op::submit(out);
op::empty() | op::submit(out);
}
});
}
......@@ -63,7 +63,7 @@ int main() {
op::just(42) | op::transform([](int i) {
if (i < 42) {
return mi::any_single_sender<std::exception_ptr, std::string>{
op::empty<std::string>()};
op::empty()};
}
return mi::any_single_sender<std::exception_ptr, std::string>{
op::just(std::to_string(i))};
......
......@@ -74,7 +74,7 @@ int main() {
op::just(42) | op::transform([](auto v) {
using r_t = mi::any_single_sender<std::exception_ptr, int>;
if (v < 40) {
return r_t{op::error<int>(std::exception_ptr{})};
return r_t{op::error(std::exception_ptr{})};
} else {
return r_t{op::just(v)};
}
......
......@@ -34,381 +34,1025 @@ using not_is_t = std::enable_if_t<!is_v<std::decay_t<T>, C>, std::decay_t<T>>;
//
// define types for executors
template <class E, class... VN>
class any_executor {
union data {
void* pobj_ = nullptr;
std::aligned_union_t< 0, std::tuple<VN...> > buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible<Wrapped>::value;
}
struct vtable {
static void s_op(data&, data*) {}
static void s_consume_op(data&&, data*) {}
static any_single_sender<E, any_executor_ref<E>> s_schedule(data&, VN...) { return {}; }
void (*op_)(data&, data*) = vtable::s_op;
void (*consume_op_)(data&, data*) = vtable::s_consume_op;
any_single_sender<E, any_executor_ref<E>> (*schedule_)(data&, VN...) = vtable::s_schedule;
};
static constexpr vtable const noop_{};
vtable const* vptr_ = &noop_;
template <class Wrapped>
any_executor(Wrapped obj, std::false_type) : any_executor() {
struct s {
static void op(data& src, data* dst) {
dst->pobj_ = new Wrapped(*static_cast<Wrapped const*>(src.pobj_));
}
static void consume_op(data&& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static any_single_sender<E, any_executor_ref<E>> schedule(data& src, VN... vn) {
return any_single_sender<E, any_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>(src.pobj_), vn...)};
}
};
static const vtable vtbl{s::op, s::consume_op, s::schedule};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_executor(Wrapped obj, std::true_type) noexcept
: any_executor() {
struct s {
static void op(data& src, data* dst) {
new (dst->buffer_)
Wrapped(*static_cast<Wrapped*>((void*)src.buffer_));
}
static void consume_op(data& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static any_single_sender<E, any_executor_ref<E>> schedule(data& src, VN... vn) {
return any_single_sender<E, any_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>((void*)src.buffer_), vn...)};
}
};
static const vtable vtbl{s::op, s::consume_op, s::schedule};
new (data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
using wrapped_t =
std::enable_if_t<!std::is_same<U, any_executor>::value, U>;
public:
using properties = property_set<is_executor<>>;
any_executor() = default;
any_executor(const any_executor& that) noexcept : any_executor() {
that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_);
}
any_executor(any_executor&& that) noexcept : any_executor() {
that.vptr_->consume_op_(std::move(that.data_), &data_);
std::swap(that.vptr_, vptr_);
}
PUSHMI_TEMPLATE(class Wrapped)
(requires Executor<wrapped_t<Wrapped>>) //
explicit any_executor(Wrapped obj) noexcept(insitu<Wrapped>())
: any_executor{std::move(obj), bool_<insitu<Wrapped>()>{}} {}
~any_executor() {
vptr_->consume_op_(std::move(data_), nullptr);
}
any_executor& operator=(const any_executor& that) noexcept {
this->~any_executor();
new ((void*)this) any_executor(that);
return *this;
}
any_executor& operator=(any_executor&& that) noexcept {
this->~any_executor();
new ((void*)this) any_executor(std::move(that));
return *this;
}
PUSHMI_TEMPLATE(class... AN)
(requires sizeof...(VN) == sizeof...(AN)) //
any_single_sender<E, any_executor_ref<E>> schedule(AN&&... an) {
// moved this check out of the requires due to a
// mismatched pack size between VN and AN on gcc.
static_assert(And<Constructible<VN, AN>...>, "arguments must be convertible");
return vptr_->schedule_(data_, VN{(AN &&) an}...);
}
};
// Class static definitions:
template <class E, class... VN>
constexpr typename any_executor<E, VN...>::vtable const
any_executor<E, VN...>::noop_;
template <class SF>
class executor<SF> {
SF sf_;
public:
using properties = property_set<is_executor<>>;
constexpr executor() = default;
constexpr explicit executor(SF sf) : sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class... AN)
(requires PUSHMI_EXP(
lazy::Invocable<SF&, AN...>)) //
auto schedule(AN&&... an) {
return sf_((AN&&)an...);
}
};
template <
PUSHMI_TYPE_CONSTRAINT(Executor) Data,
class DSF>
class executor<Data, DSF> {
Data data_;
DSF sf_;
public:
using properties = property_set_insert_t<
properties_t<Data>,
property_set<is_executor<>>>;
constexpr executor() = default;
constexpr explicit executor(Data data) : data_(std::move(data)) {}
constexpr executor(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class... AN)
(requires PUSHMI_EXP(
lazy::Invocable<DSF&, Data&, AN...>)) //
auto schedule(AN&&... an) {
return sf_(data_, (AN&&)an...);
}
};
template <>
class executor<> : public executor<ignoreSF> {
public:
executor() = default;
};
////////////////////////////////////////////////////////////////////////////////
// make_executor
PUSHMI_INLINE_VAR constexpr struct make_executor_fn {
inline auto operator()() const {
return executor<ignoreSF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires not Executor<SF>) //
auto
operator()(SF sf) const {
return executor<SF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<>&& Executor<Data>) //
auto
operator()(Data d) const {
return executor<Data, passDSF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Executor<Data>) //
auto
operator()(Data d, DSF sf) const {
return executor<Data, DSF>{std::move(d), std::move(sf)};
}
} const make_executor{};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
executor()->executor<ignoreSF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not Executor<SF>)) //
executor(SF)
->executor<SF>;
PUSHMI_TEMPLATE(class Data)
(requires True<>&& Executor<Data>) //
executor(Data)
->executor<Data, passDSF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Executor<Data>) //
executor(Data, DSF)
->executor<Data, DSF>;
#endif
template <>
struct construct_deduced<executor> : make_executor_fn {};
namespace detail {
template <class T>
using not_any_executor_ref_t = not_is_t<T, any_executor_ref>;
} // namespace detail
template<class E>
template <class E, class... VN>
struct any_executor_ref {
private:
private:
using This = any_executor_ref;
void* pobj_;
struct vtable {
void (*submit_)(void*, void*);
} const *vptr_;
any_single_sender<E, any_executor_ref<E>> (*schedule_)(void*, VN...);
} const* vptr_;
template <class T>
using wrapped_t = detail::not_any_executor_ref_t<T>;
public:
using properties = property_set<is_sender<>, is_executor<>, is_single<>>;
public:
using properties = property_set<is_executor<>>;
any_executor_ref() = delete;
any_executor_ref(const any_executor_ref&) = default;
PUSHMI_TEMPLATE (class Wrapped)
(requires Sender<wrapped_t<Wrapped>, is_executor<>, is_single<>>)
// (requires SenderTo<wrapped_t<Wrapped>, any_receiver<E, This>>)
any_executor_ref(Wrapped& w) {
// This can't be a requirement because it asks if submit(w, any_receiver<E,T>)
// is well-formed (where T is an alias for any_executor_ref). If w
// has a submit that is constrained with SingleReceiver<any_receiver<E,T>, T'&, E'>, that
// will ask whether value(any_receiver<E,T>, T'&) is well-formed. And *that* will
// ask whether T'& is convertible to T. That brings us right back to this
// constructor. Constraint recursion!
static_assert(
SenderTo<Wrapped, any_receiver<E,This>>,
"Expecting to be passed a Sender that can send to a SingleReceiver"
" that accepts a value of type This and an error of type E");
PUSHMI_TEMPLATE(class Wrapped)
(requires Executor<wrapped_t<Wrapped>>) //
any_executor_ref(Wrapped& w) {
struct s {
static void submit(void* pobj, void* s) {
return ::folly::pushmi::submit(
*static_cast<Wrapped*>(pobj),
std::move(*static_cast<any_receiver<E, This>*>(s)));
static any_single_sender<E, any_executor_ref<E>> schedule(void* pobj, VN... vn) {
return any_single_sender<E, any_executor_ref<E>>{
::folly::pushmi::schedule(*static_cast<Wrapped*>(pobj), vn...)};
}
};
static const vtable vtbl{s::submit};
static const vtable vtbl{s::schedule};
pobj_ = std::addressof(w);
vptr_ = &vtbl;
}
any_executor_ref executor() { return *this; }
template<class SingleReceiver>
void submit(SingleReceiver&& sa) {
// static_assert(
// ConvertibleTo<SingleReceiver, any_receiver<E, This>>,
// "requires any_receiver<E, any_executor_ref<E>>");
any_receiver<E, This> s{(SingleReceiver&&) sa};
vptr_->submit_(pobj_, &s);
template<class... AN>
any_single_sender<E, any_executor_ref<E>> schedule(AN&&... an) {
return vptr_->schedule_(pobj_, VN{(AN&&)an}...);
}
};
////////////////////////////////////////////////////////////////////////////////
// make_any_executor_ref
template <
class E = std::exception_ptr>
template <class E = std::exception_ptr>
auto make_any_executor_ref() {
return any_executor_ref<E>{};
}
PUSHMI_TEMPLATE (
class E = std::exception_ptr,
class Wrapped)
(requires Sender<detail::not_any_executor_ref_t<Wrapped>, is_executor<>, is_single<>>)
auto make_any_executor_ref(Wrapped& w) {
PUSHMI_TEMPLATE(class E = std::exception_ptr, class Wrapped)
(requires Executor<detail::not_any_executor_ref_t<Wrapped>>) //
auto make_any_executor_ref(Wrapped& w) {
return any_executor_ref<E>{w};
}
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
any_executor_ref() ->
any_executor_ref<
std::exception_ptr>;
PUSHMI_TEMPLATE (class Wrapped)
(requires Sender<detail::not_any_executor_ref_t<Wrapped>, is_executor<>, is_single<>>)
any_executor_ref(Wrapped&) ->
any_executor_ref<
std::exception_ptr>;
any_executor_ref()->any_executor_ref<std::exception_ptr>;
PUSHMI_TEMPLATE(class Wrapped)
(requires Executor<detail::not_any_executor_ref_t<Wrapped>>) //
any_executor_ref(Wrapped&)
->any_executor_ref<std::exception_ptr>;
#endif
namespace detail {
template<class E>
using any_executor_base =
any_single_sender<E, any_executor_ref<E>>;
template<class T, class E>
using not_any_executor =
std::enable_if_t<
!std::is_base_of<any_executor_base<E>, std::decay_t<T>>::value,
std::decay_t<T>>;
} // namespace detail
//
// define types for constrained executors
template <class E>
struct any_executor : detail::any_executor_base<E> {
constexpr any_executor() = default;
using properties = property_set<is_sender<>, is_executor<>, is_single<>>;
using detail::any_executor_base<E>::any_executor_base;
template <class E, class CV, class... VN>
class any_constrained_executor {
union data {
void* pobj_ = nullptr;
std::aligned_union_t< 0, std::tuple<CV, VN...> > buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible<Wrapped>::value;
}
struct vtable {
static void s_op(data&, data*) {}
static void s_consume_op(data&&, data*) {}
static any_single_sender<E, any_constrained_executor_ref<E>> s_schedule(data&, VN...) { std::terminate(); return {}; }
static any_single_sender<E, any_constrained_executor_ref<E>> s_schedule_cv(data&, CV, VN...) { std::terminate(); return {}; }
static CV s_top(data&) { return {}; }
void (*op_)(data&, data*) = vtable::s_op;
void (*consume_op_)(data&&, data*) = vtable::s_consume_op;
CV (*top_)(data&) = vtable::s_top;
any_single_sender<E, any_constrained_executor_ref<E>> (*schedule_)(data&, VN...) = vtable::s_schedule;
any_single_sender<E, any_constrained_executor_ref<E>> (*schedule_cv_)(data&, CV, VN...) = vtable::s_schedule_cv;
};
static constexpr vtable const noop_{};
vtable const* vptr_ = &noop_;
template <class Wrapped>
any_constrained_executor(Wrapped obj, std::false_type) : any_constrained_executor() {
struct s {
static void op(data& src, data* dst) {
dst->pobj_ = new Wrapped(*static_cast<Wrapped const*>(src.pobj_));
}
static void consume_op(data&& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static CV top(data& src) {
return ::folly::pushmi::top(*static_cast<Wrapped*>(src.pobj_));
}
static any_single_sender<E, any_constrained_executor_ref<E>> schedule(data& src, VN... vn) {
return any_single_sender<E, any_constrained_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>(src.pobj_), vn...)};
}
static any_single_sender<E, any_constrained_executor_ref<E>> schedule_cv(data& src, CV cv, VN... vn) {
return any_single_sender<E, any_constrained_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>(src.pobj_), cv, vn...)};
}
};
static const vtable vtbl{s::op, s::consume_op, s::top, s::schedule, s::schedule_cv};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_constrained_executor(Wrapped obj, std::true_type) noexcept
: any_constrained_executor() {
struct s {
static void op(data& src, data* dst) {
new (dst->buffer_)
Wrapped(*static_cast<Wrapped*>((void*)src.buffer_));
}
static void consume_op(data&& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static CV top(data& src) {
return ::folly::pushmi::top(*static_cast<Wrapped*>((void*)src.buffer_));
}
static any_single_sender<E, any_constrained_executor_ref<E>> schedule(data& src, VN... vn) {
return any_single_sender<E, any_constrained_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>((void*)src.buffer_), vn...)};
}
static any_single_sender<E, any_constrained_executor_ref<E>> schedule_cv(data& src, CV cv, VN... vn) {
return any_single_sender<E, any_constrained_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>((void*)src.buffer_), cv, vn...)};
}
};
static const vtable vtbl{s::op, s::consume_op, s::top, s::schedule};
new (data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
using wrapped_t =
std::enable_if_t<!std::is_same<U, any_constrained_executor>::value, U>;
public:
using properties = property_set<is_constrained<>>;
any_constrained_executor() = default;
any_constrained_executor(const any_constrained_executor& that) noexcept : any_constrained_executor() {
that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_);
}
any_constrained_executor(any_constrained_executor&& that) noexcept : any_constrained_executor() {
that.vptr_->consume_op_(std::move(that.data_), &data_);
std::swap(that.vptr_, vptr_);
}
PUSHMI_TEMPLATE(class Wrapped)
(requires ConstrainedExecutor<wrapped_t<Wrapped>>) //
explicit any_constrained_executor(Wrapped obj) noexcept(insitu<Wrapped>())
: any_constrained_executor{std::move(obj), bool_<insitu<Wrapped>()>{}} {}
~any_constrained_executor() {
vptr_->consume_op_(std::move(data_), nullptr);
}
any_constrained_executor& operator=(const any_constrained_executor& that) noexcept {
this->~any_constrained_executor();
new ((void*)this) any_constrained_executor(that);
return *this;
}
any_constrained_executor& operator=(any_constrained_executor&& that) noexcept {
this->~any_constrained_executor();
new ((void*)this) any_constrained_executor(std::move(that));
return *this;
}
CV top() {
return vptr_->top_(data_);
}
PUSHMI_TEMPLATE(class... AN)
(requires And<Constructible<VN, AN>...>)
any_single_sender<E, any_constrained_executor_ref<E>> schedule(AN&&... an) {
return vptr_->schedule_(data_, VN{(AN &&) an}...);
}
PUSHMI_TEMPLATE(class... AN)
(requires And<Constructible<VN, AN>...>)
any_single_sender<E, any_constrained_executor_ref<E>> schedule(CV cv, AN&&... an) {
return vptr_->schedule_cv_(data_, cv, VN{(AN &&) an}...);
}
};
// Class static definitions:
template <class E, class CV, class... VN>
constexpr typename any_constrained_executor<E, CV, VN...>::vtable const
any_constrained_executor<E, CV, VN...>::noop_;
template <class SF, class ZF>
class constrained_executor<SF, ZF> {
SF sf_;
ZF zf_;
public:
using properties = property_set<is_constrained<>>;
constexpr constrained_executor() = default;
constexpr explicit constrained_executor(SF sf) : sf_(std::move(sf)) {}
constexpr constrained_executor(SF sf, ZF zf) : sf_(std::move(sf)), zf_(std::move(zf)) {}
invoke_result_t<ZF&> top() {
return zf_();
}
PUSHMI_TEMPLATE(class... AN)
(requires PUSHMI_EXP(
lazy::Invocable<SF&, AN...>)) //
auto schedule(AN&&... an) {
return sf_((AN&&)an...);
}
};
////////////////////////////////////////////////////////////////////////////////
// make_any_executor
template <
class E = std::exception_ptr>
auto make_any_executor() -> any_executor<E> {
return any_executor<E>{};
}
PUSHMI_TYPE_CONSTRAINT(ConstrainedExecutor) Data,
class DSF,
class DZF>
class constrained_executor<Data, DSF, DZF> {
Data data_;
DSF sf_;
DZF zf_;
PUSHMI_TEMPLATE(
class E = std::exception_ptr,
class Wrapped)
(requires SenderTo<
detail::not_any_executor<Wrapped, E>,
any_receiver<E, any_executor_ref<E>>>)
auto make_any_executor(Wrapped w) -> any_executor<E> {
return any_executor<E>{std::move(w)};
}
public:
using properties = property_set_insert_t<
properties_t<Data>,
property_set<is_constrained<>>>;
constexpr constrained_executor() = default;
constexpr explicit constrained_executor(Data data) : data_(std::move(data)) {}
constexpr constrained_executor(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
constexpr constrained_executor(Data data, DSF sf, DZF zf)
: data_(std::move(data)), sf_(std::move(sf)), zf_(std::move(zf)) {}
invoke_result_t<DZF&, Data&> top() {
return zf_(data_);
}
PUSHMI_TEMPLATE(class... AN)
(requires PUSHMI_EXP(
lazy::Invocable<DSF&, Data&, AN...>)) //
auto schedule(AN&&... an) {
return sf_(data_, (AN&&)an...);
}
};
template <>
class constrained_executor<> : public constrained_executor<ignoreSF, priorityZeroF> {
public:
constrained_executor() = default;
};
////////////////////////////////////////////////////////////////////////////////
// make_constrained_executor
PUSHMI_INLINE_VAR constexpr struct make_constrained_executor_fn {
inline auto operator()() const {
return constrained_executor<ignoreSF, priorityZeroF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not ConstrainedExecutor<SF>)) //
auto
operator()(SF sf) const {
return constrained_executor<SF, priorityZeroF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class SF, class ZF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not ConstrainedExecutor<SF>)) //
auto
operator()(SF sf, ZF zf) const {
return constrained_executor<SF, ZF>{std::move(sf), std::move(zf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<>&& ConstrainedExecutor<Data>) //
auto
operator()(Data d) const {
return constrained_executor<Data, passDSF, passDZF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires ConstrainedExecutor<Data>) //
auto
operator()(Data d, DSF sf) const {
return constrained_executor<Data, DSF, passDZF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE(class Data, class DSF, class DZF)
(requires ConstrainedExecutor<Data>) //
auto
operator()(Data d, DSF sf, DZF zf) const {
return constrained_executor<Data, DSF, DZF>{std::move(d), std::move(sf), std::move(zf)};
}
} const make_constrained_executor{};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
any_executor() ->
any_executor<
std::exception_ptr>;
constrained_executor()->constrained_executor<ignoreSF, priorityZeroF>;
PUSHMI_TEMPLATE(class Wrapped)
(requires SenderTo<
detail::not_any_executor<
Wrapped,
std::exception_ptr>,
any_receiver<
std::exception_ptr,
any_executor_ref<
std::exception_ptr>>>)
any_executor(Wrapped) ->
any_executor<
std::exception_ptr>;
#endif
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not ConstrainedExecutor<SF>)) //
constrained_executor(SF)
->constrained_executor<SF, priorityZeroF>;
PUSHMI_TEMPLATE(class SF, class ZF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not ConstrainedExecutor<SF>)) //
constrained_executor(SF, ZF)
->constrained_executor<SF, ZF>;
//
// define types for constrained executors
PUSHMI_TEMPLATE(class Data)
(requires True<>&& ConstrainedExecutor<Data>) //
constrained_executor(Data)
->constrained_executor<Data, passDSF, passDZF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires ConstrainedExecutor<Data>) //
constrained_executor(Data, DSF)
->constrained_executor<Data, DSF, passDZF>;
PUSHMI_TEMPLATE(class Data, class DSF, class DZF)
(requires ConstrainedExecutor<Data>) //
constrained_executor(Data, DSF, DZF)
->constrained_executor<Data, DSF, DZF>;
#endif
template <>
struct construct_deduced<constrained_executor> : make_constrained_executor_fn {};
namespace detail {
template <class T>
using not_any_constrained_executor_ref_t = not_is_t<T, any_constrained_executor_ref>;
using not_any_constrained_executor_ref_t =
not_is_t<T, any_constrained_executor_ref>;
} // namespace detail
template<class E, class CV>
template <class E, class CV, class... VN>
struct any_constrained_executor_ref {
private:
private:
using This = any_constrained_executor_ref;
void* pobj_;
struct vtable {
CV (*top_)(void*);
void (*submit_)(void*, CV, void*);
} const *vptr_;
any_single_sender<E, CV, any_constrained_executor_ref<E, CV>> (*schedule_)(
void*, VN...);
any_single_sender<E, CV, any_constrained_executor_ref<E, CV>> (
*schedule_cv_)(void*, CV, VN...);
} const* vptr_;
template <class T>
using wrapped_t = detail::not_any_constrained_executor_ref_t<T>;
public:
using properties = property_set<is_constrained<>, is_executor<>, is_single<>>;
public:
using properties = property_set<is_constrained<>>;
any_constrained_executor_ref() = delete;
any_constrained_executor_ref(const any_constrained_executor_ref&) = default;
PUSHMI_TEMPLATE (class Wrapped)
(requires ConstrainedSender<wrapped_t<Wrapped>, is_single<>>)
// (requires ConstrainedSenderTo<wrapped_t<Wrapped>, any_receiver<E,This>>)
any_constrained_executor_ref(Wrapped& w) {
// This can't be a requirement because it asks if submit(w, top(w), any_receiver<E,T>)
// is well-formed (where T is an alias for any_constrained_executor_ref). If w
// has a submit that is constrained with ReceiveValue<any_receiver<E,T>, T'&>, that
// will ask whether value(any_receiver<E,T>, T'&) is well-formed. And *that* will
// ask whether T'& is convertible to T. That brings us right back to this
// constructor. Constraint recursion!
static_assert(
ConstrainedSenderTo<Wrapped, any_receiver<E,This>>,
"Expecting to be passed a ConstrainedSender that can send to a SingleReceiver"
" that accpets a value of type This and an error of type E");
PUSHMI_TEMPLATE(class Wrapped)
(requires ConstrainedExecutor<wrapped_t<Wrapped>>)
any_constrained_executor_ref(Wrapped& w) {
struct s {
static CV top(void* pobj) {
return ::folly::pushmi::top(*static_cast<Wrapped*>(pobj));
}
static void submit(void* pobj, CV cv, void* s) {
return ::folly::pushmi::submit(
static any_single_sender<E, CV, any_constrained_executor_ref<E, CV>>
schedule(void* pobj, VN... vn) {
return ::folly::pushmi::schedule(
*static_cast<Wrapped*>(pobj),
cv,
std::move(*static_cast<any_receiver<E, This>*>(s)));
::folly::pushmi::top(*static_cast<Wrapped*>(pobj)), vn...);
}
static any_single_sender<E, CV, any_constrained_executor_ref<E, CV>>
schedule_cv(void* pobj, CV cv, VN... vn) {
return ::folly::pushmi::schedule(*static_cast<Wrapped*>(pobj), cv, vn...);
}
};
static const vtable vtbl{s::top, s::submit};
static const vtable vtbl{s::top, s::schedule, s::schedule_cv};
pobj_ = std::addressof(w);
vptr_ = &vtbl;
}
CV top() {
return vptr_->top_(pobj_);
}
any_constrained_executor_ref executor() { return *this; }
template<class SingleReceiver>
void submit(CV cv, SingleReceiver&& sa) {
// static_assert(
// ConvertibleTo<SingleReceiver, any_receiver<E, This>>,
// "requires any_receiver<E, any_constrained_executor_ref<E, TP>>");
any_receiver<E, This> s{(SingleReceiver&&) sa};
vptr_->submit_(pobj_, cv, &s);
PUSHMI_TEMPLATE(class... AN)
(requires sizeof...(VN) == sizeof...(AN)) //
any_single_sender<E, any_constrained_executor_ref<E>> schedule(AN&&... an) {
// moved this check out of the requires due to a
// mismatched pack size between VN and AN on gcc.
static_assert(And<Constructible<VN, AN>...>, "arguments must be convertible");
return vptr_->schedule_(pobj_, VN{(AN&&)an}...);
}
PUSHMI_TEMPLATE(class... AN)
(requires sizeof...(VN) == sizeof...(AN)) //
any_single_sender<E, any_constrained_executor_ref<E>> schedule(CV cv, AN&&... an) {
// moved this check out of the requires due to a
// mismatched pack size between VN and AN on gcc.
static_assert(And<Constructible<VN, AN>...>, "arguments must be convertible");
return vptr_->schedule_cv_(pobj_, cv, VN{(AN&&)an}...);
}
};
////////////////////////////////////////////////////////////////////////////////
// make_any_constrained_executor_ref
template <
class E = std::exception_ptr,
class CV = std::ptrdiff_t>
template <class E = std::exception_ptr, class CV = std::ptrdiff_t>
auto make_any_constrained_executor_ref() {
return any_constrained_executor_ref<E, CV>{};
}
PUSHMI_TEMPLATE (
class E = std::exception_ptr,
class Wrapped)
(requires ConstrainedSender<detail::not_any_constrained_executor_ref_t<Wrapped>, is_single<>>)
auto make_any_constrained_executor_ref(Wrapped& w) {
PUSHMI_TEMPLATE(class E = std::exception_ptr, class Wrapped)
(requires ConstrainedExecutor<
detail::not_any_constrained_executor_ref_t<Wrapped>>) //
auto make_any_constrained_executor_ref(Wrapped& w) {
return any_constrained_executor_ref<E, constraint_t<Wrapped>>{w};
}
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
any_constrained_executor_ref() ->
any_constrained_executor_ref<
std::exception_ptr,
std::ptrdiff_t>;
any_constrained_executor_ref()
->any_constrained_executor_ref<std::exception_ptr, std::ptrdiff_t>;
PUSHMI_TEMPLATE (class Wrapped)
(requires ConstrainedSender<detail::not_any_constrained_executor_ref_t<Wrapped>, is_single<>>)
any_constrained_executor_ref(Wrapped&) ->
any_constrained_executor_ref<
std::exception_ptr,
constraint_t<Wrapped>>;
PUSHMI_TEMPLATE(class Wrapped)
(requires ConstrainedExecutor<
detail::not_any_constrained_executor_ref_t<Wrapped>>) //
any_constrained_executor_ref(Wrapped&)
->any_constrained_executor_ref<
std::exception_ptr,
constraint_t<Wrapped>>;
#endif
namespace detail {
template<class E, class CV>
using any_constrained_executor_base =
any_constrained_single_sender<E, CV, any_constrained_executor_ref<E, CV>>;
template<class T, class E, class CV>
using not_any_constrained_executor =
std::enable_if_t<
!std::is_base_of<any_constrained_executor_base<E, CV>, std::decay_t<T>>::value,
std::decay_t<T>>;
} // namespace detail
//
// define types for time executors
template <class E, class CV>
struct any_constrained_executor : detail::any_constrained_executor_base<E, CV> {
using properties = property_set<is_constrained<>, is_executor<>, is_single<>>;
constexpr any_constrained_executor() = default;
using detail::any_constrained_executor_base<E, CV>::any_constrained_executor_base;
template <class E, class TP, class... VN>
class any_time_executor {
union data {
void* pobj_ = nullptr;
std::aligned_union_t< 0, std::tuple<TP, VN...> > buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible<Wrapped>::value;
}
struct vtable {
static void s_op(data&, data*) {}
static void s_consume_op(data&&, data*) {}
static any_single_sender<E, any_time_executor_ref<E>> s_schedule(data&, VN...) { std::terminate(); return {}; }
static any_single_sender<E, any_time_executor_ref<E>> s_schedule_time(data&, TP, VN...) { std::terminate(); return {}; }
static TP s_now(data&) { return {}; }
void (*op_)(data&, data*) = vtable::s_op;
void (*consume_op_)(data&&, data*) = vtable::s_consume_op;
TP (*now_)(data&) = vtable::s_now;
any_single_sender<E, any_time_executor_ref<E>> (*schedule_)(data&, VN...) = vtable::s_schedule;
any_single_sender<E, any_time_executor_ref<E>> (*schedule_time_)(data&, TP, VN...) = vtable::s_schedule_time;
};
static constexpr vtable const noop_{};
vtable const* vptr_ = &noop_;
template <class Wrapped>
any_time_executor(Wrapped obj, std::false_type) : any_time_executor() {
struct s {
static void op(data& src, data* dst) {
dst->pobj_ = new Wrapped(*static_cast<Wrapped const*>(src.pobj_));
}
static void consume_op(data&& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static TP now(data& src) {
return ::folly::pushmi::now(*static_cast<Wrapped*>(src.pobj_));
}
static any_single_sender<E, any_time_executor_ref<E>> schedule(data& src, VN... vn) {
return any_single_sender<E, any_time_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>(src.pobj_), vn...)};
}
static any_single_sender<E, any_time_executor_ref<E>> schedule_time(data& src, TP tp, VN... vn) {
return any_single_sender<E, any_time_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>(src.pobj_), tp, vn...)};
}
};
static const vtable vtbl{s::op, s::consume_op, s::now, s::schedule, s::schedule_time};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_time_executor(Wrapped obj, std::true_type) noexcept
: any_time_executor() {
struct s {
static void op(data& src, data* dst) {
new (dst->buffer_)
Wrapped(*static_cast<Wrapped*>((void*)src.buffer_));
}
static void consume_op(data&& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static TP now(data& src) {
return ::folly::pushmi::now(*static_cast<Wrapped*>((void*)src.buffer_));
}
static any_single_sender<E, any_time_executor_ref<E>> schedule(data& src, VN... vn) {
return any_single_sender<E, any_time_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>((void*)src.buffer_), vn...)};
}
static any_single_sender<E, any_time_executor_ref<E>> schedule_time(data& src, TP tp, VN... vn) {
return any_single_sender<E, any_time_executor_ref<E>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>((void*)src.buffer_), tp, vn...)};
}
};
static const vtable vtbl{s::op, s::consume_op, s::now, s::schedule, s::schedule_time};
new (data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
using wrapped_t =
std::enable_if_t<!std::is_same<U, any_time_executor>::value, U>;
public:
using properties = property_set<is_time<>>;
any_time_executor() = default;
any_time_executor(const any_time_executor& that) noexcept : any_time_executor() {
that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_);
}
any_time_executor(any_time_executor&& that) noexcept : any_time_executor() {
that.vptr_->consume_op_(std::move(that.data_), &data_);
std::swap(that.vptr_, vptr_);
}
PUSHMI_TEMPLATE(class Wrapped)
(requires TimeExecutor<wrapped_t<Wrapped>>) //
explicit any_time_executor(Wrapped obj) noexcept(insitu<Wrapped>())
: any_time_executor{std::move(obj), bool_<insitu<Wrapped>()>{}} {}
~any_time_executor() {
vptr_->consume_op_(std::move(data_), nullptr);
}
any_time_executor& operator=(const any_time_executor& that) noexcept {
this->~any_time_executor();
new ((void*)this) any_time_executor(that);
return *this;
}
any_time_executor& operator=(any_time_executor&& that) noexcept {
this->~any_time_executor();
new ((void*)this) any_time_executor(std::move(that));
return *this;
}
TP top() {
return vptr_->now_(data_);
}
PUSHMI_TEMPLATE(class... AN)
(requires And<Constructible<VN, AN>...>)
any_single_sender<E, any_time_executor_ref<E>> schedule(AN&&... an) {
return vptr_->schedule_(data_, VN{(AN &&) an}...);
}
PUSHMI_TEMPLATE(class... AN)
(requires And<Constructible<VN, AN>...>)
any_single_sender<E, any_time_executor_ref<E>> schedule(TP tp, AN&&... an) {
return vptr_->schedule_time_(data_, tp, VN{(AN &&) an}...);
}
};
// Class static definitions:
template <class E, class TP, class... VN>
constexpr typename any_time_executor<E, TP, VN...>::vtable const
any_time_executor<E, TP, VN...>::noop_;
template <class SF, class NF>
class time_executor<SF, NF> {
SF sf_;
NF nf_;
public:
using properties = property_set<is_time<>>;
constexpr time_executor() = default;
constexpr explicit time_executor(SF sf) : sf_(std::move(sf)) {}
constexpr time_executor(SF sf, NF nf) : sf_(std::move(sf)), nf_(std::move(nf)) {}
invoke_result_t<NF&> top() {
return nf_();
}
PUSHMI_TEMPLATE(class... AN)
(requires PUSHMI_EXP(
lazy::Invocable<SF&, AN...>)) //
auto schedule(AN&&... an) {
return sf_((AN&&)an...);
}
};
////////////////////////////////////////////////////////////////////////////////
// make_any_constrained_executor
template <
class E = std::exception_ptr,
class CV = std::ptrdiff_t>
auto make_any_constrained_executor() -> any_constrained_executor<E, CV> {
return any_constrained_executor<E, CV>{};
}
PUSHMI_TYPE_CONSTRAINT(TimeExecutor) Data,
class DSF,
class DNF>
class time_executor<Data, DSF, DNF> {
Data data_;
DSF sf_;
DNF nf_;
PUSHMI_TEMPLATE(
class E = std::exception_ptr,
class Wrapped)
(requires ConstrainedSenderTo<
detail::not_any_constrained_executor<Wrapped, E, constraint_t<Wrapped>>,
any_receiver<E, any_constrained_executor_ref<E, constraint_t<Wrapped>>>>)
auto make_any_constrained_executor(Wrapped w) -> any_constrained_executor<E, constraint_t<Wrapped>> {
return any_constrained_executor<E, constraint_t<Wrapped>>{std::move(w)};
}
public:
using properties = property_set_insert_t<
properties_t<Data>,
property_set<is_time<>>>;
constexpr time_executor() = default;
constexpr explicit time_executor(Data data) : data_(std::move(data)) {}
constexpr time_executor(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
constexpr time_executor(Data data, DSF sf, DNF nf)
: data_(std::move(data)), sf_(std::move(sf)), nf_(std::move(nf)) {}
invoke_result_t<DNF&, Data&> top() {
return nf_(data_);
}
PUSHMI_TEMPLATE(class... AN)
(requires PUSHMI_EXP(
lazy::Invocable<DSF&, Data&, AN...>)) //
auto schedule(AN&&... an) {
return sf_(data_, (AN&&)an...);
}
};
template <>
class time_executor<> : public time_executor<ignoreSF, systemNowF> {
public:
time_executor() = default;
};
////////////////////////////////////////////////////////////////////////////////
// make_time_executor
PUSHMI_INLINE_VAR constexpr struct make_time_executor_fn {
inline auto operator()() const {
return time_executor<ignoreSF, systemNowF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not TimeExecutor<SF>)) //
auto
operator()(SF sf) const {
return time_executor<SF, systemNowF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class SF, class NF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not TimeExecutor<SF>)) //
auto
operator()(SF sf, NF nf) const {
return time_executor<SF, NF>{std::move(sf), std::move(nf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<>&& TimeExecutor<Data>) //
auto
operator()(Data d) const {
return time_executor<Data, passDSF, passDNF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires TimeExecutor<Data>) //
auto
operator()(Data d, DSF sf) const {
return time_executor<Data, DSF, passDNF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE(class Data, class DSF, class DNF)
(requires TimeExecutor<Data>) //
auto
operator()(Data d, DSF sf, DNF nf) const {
return time_executor<Data, DSF, DNF>{std::move(d), std::move(sf), std::move(nf)};
}
} const make_time_executor{};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
any_constrained_executor() ->
any_constrained_executor<
std::exception_ptr,
std::ptrdiff_t>;
time_executor()->time_executor<ignoreSF, systemNowF>;
PUSHMI_TEMPLATE(class Wrapped)
(requires ConstrainedSenderTo<
detail::not_any_constrained_executor<
Wrapped,
std::exception_ptr,
constraint_t<Wrapped>>,
any_receiver<
std::exception_ptr,
any_constrained_executor_ref<
std::exception_ptr,
constraint_t<Wrapped>>>>)
any_constrained_executor(Wrapped) ->
any_constrained_executor<
std::exception_ptr,
constraint_t<Wrapped>>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not TimeExecutor<SF>)) //
time_executor(SF)
->time_executor<SF, systemNowF>;
PUSHMI_TEMPLATE(class SF, class NF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not TimeExecutor<SF>)) //
time_executor(SF, NF)
->time_executor<SF, NF>;
PUSHMI_TEMPLATE(class Data)
(requires True<>&& TimeExecutor<Data>) //
time_executor(Data)
->time_executor<Data, passDSF, passDNF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires TimeExecutor<Data>) //
time_executor(Data, DSF)
->time_executor<Data, DSF, passDNF>;
PUSHMI_TEMPLATE(class Data, class DSF, class DNF)
(requires TimeExecutor<Data>) //
time_executor(Data, DSF, DNF)
->time_executor<Data, DSF, DNF>;
#endif
//
// define types for time executors
template <>
struct construct_deduced<time_executor> : make_time_executor_fn {};
namespace detail {
template <class T>
using not_any_time_executor_ref_t = not_is_t<T, any_time_executor_ref>;
} // namespace detail
template<class E, class TP>
template <class E, class TP, class... VN>
struct any_time_executor_ref {
private:
private:
using This = any_time_executor_ref;
void* pobj_;
struct vtable {
TP (*now_)(void*);
void (*submit_)(void*, TP, void*);
} const *vptr_;
any_single_sender<E, any_time_executor_ref<E, TP>> (*schedule_)(void*, VN...);
any_single_sender<E, any_time_executor_ref<E, TP>> (
*schedule_tp_)(void*, TP, VN...);
} const* vptr_;
template <class T>
using wrapped_t = detail::not_any_time_executor_ref_t<T>;
public:
using properties = property_set<is_time<>, is_executor<>, is_single<>>;
public:
using properties = property_set<is_time<>>;
any_time_executor_ref() = delete;
any_time_executor_ref(const any_time_executor_ref&) = default;
PUSHMI_TEMPLATE (class Wrapped)
(requires TimeSender<wrapped_t<Wrapped>, is_single<>>)
// (requires TimeSenderTo<wrapped_t<Wrapped>, receiver<E, This>>)
any_time_executor_ref(Wrapped& w) {
// This can't be a requirement because it asks if submit(w, now(w), any_receiver<E, T>)
// is well-formed (where T is an alias for any_time_executor_ref). If w
// has a submit that is constrained with ReceiverValue<any_receiver<E, T>, T'&>, that
// will ask whether value(any_receiver<E, T>, T'&) is well-formed. And *that* will
// ask whether T'& is convertible to T. That brings us right back to this
// constructor. Constraint recursion!
static_assert(
TimeSenderTo<Wrapped, any_receiver<E, This>>,
"Expecting to be passed a TimeSender that can send to a SingleReceiver"
" that accpets a value of type This and an error of type E");
PUSHMI_TEMPLATE(class Wrapped)
(requires TimeExecutor<wrapped_t<Wrapped>>)
any_time_executor_ref(Wrapped& w) {
struct s {
static TP now(void* pobj) {
return ::folly::pushmi::now(*static_cast<Wrapped*>(pobj));
}
static void submit(void* pobj, TP tp, void* s) {
return ::folly::pushmi::submit(
static any_single_sender<E, any_time_executor_ref<E, TP>> schedule(
void* pobj, VN... vn) {
return any_single_sender<E, any_time_executor_ref<E, TP>>{::folly::pushmi::schedule(
*static_cast<Wrapped*>(pobj),
tp,
std::move(*static_cast<any_receiver<E, This>*>(s)));
::folly::pushmi::now(*static_cast<Wrapped*>(pobj)), vn...)};
}
static any_single_sender<E, any_time_executor_ref<E, TP>> schedule_tp(
void* pobj,
TP tp,
VN... vn) {
return any_single_sender<E, any_time_executor_ref<E, TP>>{::folly::pushmi::schedule(*static_cast<Wrapped*>(pobj), tp, vn...)};
}
};
static const vtable vtbl{s::now, s::submit};
static const vtable vtbl{s::now, s::schedule, s::schedule_tp};
pobj_ = std::addressof(w);
vptr_ = &vtbl;
}
std::chrono::system_clock::time_point top() {
PUSHMI_TEMPLATE(class Wrapped)
(requires TimeExecutor<wrapped_t<Wrapped>> && std::is_rvalue_reference<Wrapped>::value)
any_time_executor_ref(Wrapped&& w) {
struct s {
static TP now(void* pobj) {
return ::folly::pushmi::now(*static_cast<Wrapped*>(pobj));
}
static any_single_sender<E, any_time_executor_ref<E, TP>> schedule(
void* pobj, VN... vn) {
return any_single_sender<E, any_time_executor_ref<E, TP>>{::folly::pushmi::schedule(
std::move(*static_cast<Wrapped*>(pobj)),
::folly::pushmi::now(*static_cast<Wrapped*>(pobj)), vn...)};
}
static any_single_sender<E, any_time_executor_ref<E, TP>> schedule_tp(
void* pobj,
TP tp,
VN... vn) {
return any_single_sender<E, any_time_executor_ref<E, TP>>{::folly::pushmi::schedule(
std::move(*static_cast<Wrapped*>(pobj)), tp, vn...)};
}
};
static const vtable vtbl{s::now, s::schedule, s::schedule_tp};
pobj_ = std::addressof(w);
vptr_ = &vtbl;
}
TP top() {
return vptr_->now_(pobj_);
}
any_time_executor_ref executor() { return *this; }
template<class SingleReceiver>
void submit(TP tp, SingleReceiver&& sa) {
// static_assert(
// ConvertibleTo<SingleReceiver, any_receiver<E, This>>,
// "requires any_receiver<E, any_time_executor_ref<E, TP>>");
any_receiver<E, This> s{(SingleReceiver&&) sa};
vptr_->submit_(pobj_, tp, &s);
PUSHMI_TEMPLATE(class... AN)
(requires sizeof...(VN) == sizeof...(AN)) //
any_single_sender<E, any_time_executor_ref<E, TP>> schedule(AN&&... an) {
// moved this check out of the requires due to a
// mismatched pack size between VN and AN on gcc.
static_assert(And<Constructible<VN, AN>...>, "arguments must be convertible");
return vptr_->schedule_(pobj_, VN{(AN&&)an}...);
}
PUSHMI_TEMPLATE(class... AN)
(requires sizeof...(VN) == sizeof...(AN)) //
any_single_sender<E, any_time_executor_ref<E, TP>> schedule(TP tp, AN&&... an) {
// moved this check out of the requires due to a
// mismatched pack size between VN and AN on gcc.
static_assert(And<Constructible<VN, AN>...>, "arguments must be convertible");
return vptr_->schedule_tp_(pobj_, tp, VN{(AN&&)an}...);
}
};
......@@ -421,91 +1065,24 @@ auto make_any_time_executor_ref() {
return any_time_executor_ref<E, TP>{};
}
PUSHMI_TEMPLATE (
class E = std::exception_ptr,
class Wrapped)
(requires TimeSender<detail::not_any_time_executor_ref_t<Wrapped>, is_single<>>)
auto make_any_time_executor_ref(Wrapped& w) {
PUSHMI_TEMPLATE(class E = std::exception_ptr, class Wrapped)
(requires TimeExecutor<detail::not_any_time_executor_ref_t<Wrapped>>) //
auto make_any_time_executor_ref(Wrapped& w) {
return any_time_executor_ref<E, time_point_t<Wrapped>>{w};
}
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
any_time_executor_ref() ->
any_time_executor_ref<
std::exception_ptr,
std::chrono::system_clock::time_point>;
PUSHMI_TEMPLATE (class Wrapped)
(requires TimeSender<detail::not_any_time_executor_ref_t<Wrapped>, is_single<>>)
any_time_executor_ref(Wrapped&) ->
any_time_executor_ref<
std::exception_ptr,
time_point_t<Wrapped>>;
#endif
namespace detail {
template<class E, class TP>
using any_time_executor_base =
any_time_single_sender<E, TP, any_time_executor_ref<E, TP>>;
template<class T, class E, class TP>
using not_any_time_executor =
std::enable_if_t<
!std::is_base_of<any_time_executor_base<E, TP>, std::decay_t<T>>::value,
std::decay_t<T>>;
} // namespace detail
template <class E, class TP>
struct any_time_executor : detail::any_time_executor_base<E, TP> {
using properties = property_set<is_time<>, is_executor<>, is_single<>>;
constexpr any_time_executor() = default;
using detail::any_time_executor_base<E, TP>::any_time_executor_base;
};
////////////////////////////////////////////////////////////////////////////////
// make_any_time_executor
template <
class E = std::exception_ptr,
class TP = std::chrono::system_clock::time_point>
auto make_any_time_executor() -> any_time_executor<E, TP> {
return any_time_executor<E, TP>{};
}
PUSHMI_TEMPLATE(
class E = std::exception_ptr,
class Wrapped)
(requires TimeSenderTo<
detail::not_any_time_executor<Wrapped, E, time_point_t<Wrapped>>,
any_receiver<E, any_time_executor_ref<E, time_point_t<Wrapped>>>>)
auto make_any_time_executor(Wrapped w) -> any_time_executor<E, time_point_t<Wrapped>> {
return any_time_executor<E, time_point_t<Wrapped>>{std::move(w)};
}
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
any_time_executor() ->
any_time_executor<
any_time_executor_ref()
->any_time_executor_ref<
std::exception_ptr,
std::chrono::system_clock::time_point>;
PUSHMI_TEMPLATE(class Wrapped)
(requires TimeSenderTo<
detail::not_any_time_executor<
Wrapped,
std::exception_ptr,
time_point_t<Wrapped>>,
any_receiver<
std::exception_ptr,
any_time_executor_ref<
std::exception_ptr,
time_point_t<Wrapped>>>>)
any_time_executor(Wrapped) ->
any_time_executor<
std::exception_ptr,
time_point_t<Wrapped>>;
(requires TimeExecutor<detail::not_any_time_executor_ref_t<Wrapped>>) //
any_time_executor_ref(Wrapped&)
->any_time_executor_ref<std::exception_ptr, time_point_t<Wrapped>>;
#endif
} // namespace pushmi
......
......@@ -31,67 +31,82 @@ namespace __adl {
//
PUSHMI_TEMPLATE(class S)
(requires requires( //
std::declval<S&>().done())) //
(requires //
requires(std::declval<S&>().done())) //
void set_done(S& s) //
noexcept(noexcept(s.done())) {
noexcept(noexcept((s).done())) {
s.done();
}
PUSHMI_TEMPLATE(class S, class E)
(requires requires( //
std::declval<S&>().error(std::declval<E>()))) //
(requires //
requires(std::declval<S&>().error(std::declval<E>()))) //
void set_error(S& s, E e) //
noexcept(noexcept(s.error(std::move(e)))) {
noexcept(noexcept((s).error(std::move(e)))) {
s.error(std::move(e));
}
PUSHMI_TEMPLATE(class S, class... VN)
(requires requires( //
std::declval<S&>().value(std::declval<VN&&>()...))) //
(requires //
requires(std::declval<S&>().value(std::declval<VN>()...))) //
void set_value(S& s, VN&&... vn) //
noexcept(noexcept(s.value((VN &&) vn...))) {
s.value((VN &&) vn...);
}
PUSHMI_TEMPLATE(class S, class Up)
(requires requires( //
std::declval<S&>().starting(std::declval<Up&&>()))) //
(requires //
requires(std::declval<S&>().starting(std::declval<Up>()))) //
void set_starting(S& s, Up&& up) //
noexcept(noexcept(s.starting((Up &&) up))) {
s.starting((Up &&) up);
}
PUSHMI_TEMPLATE(class SD)
(requires requires( //
std::declval<SD&>().executor())) //
auto executor(SD& sd) //
(requires //
requires(std::declval<SD&>().executor())) //
auto get_executor(SD& sd) //
noexcept(noexcept(sd.executor())) {
return sd.executor();
}
PUSHMI_TEMPLATE(class SD)
(requires //
requires(std::declval<SD&>().make_strand())) //
auto make_strand(SD& sd) //
noexcept(noexcept(sd.make_strand())) {
return sd.make_strand();
}
PUSHMI_TEMPLATE(class SD, class Out)
(requires requires( //
std::declval<SD&>().submit(std::declval<Out>()))) //
void submit(SD& sd, Out out) //
noexcept(noexcept(sd.submit(std::move(out)))) {
sd.submit(std::move(out));
(requires //
requires(std::declval<SD>().submit(std::declval<Out>()))) //
void submit(SD&& sd, Out&& out) //
noexcept(noexcept(((SD &&) sd).submit((Out &&) out))) {
((SD &&) sd).submit((Out &&) out);
}
PUSHMI_TEMPLATE(class SD)
(requires requires( //
std::declval<SD&>().top())) //
(requires //
requires(std::declval<SD&>().top())) //
auto top(SD& sd) //
noexcept(noexcept(sd.top())) {
return sd.top();
}
PUSHMI_TEMPLATE(class SD, class TP, class Out)
(requires requires( //
std::declval<SD&>().submit(
std::declval<TP (&)(TP)>()(top(std::declval<SD&>())),
std::declval<Out>()))) //
void submit(SD& sd, TP tp, Out out) //
noexcept(noexcept(sd.submit(std::move(tp), std::move(out)))) {
sd.submit(std::move(tp), std::move(out));
PUSHMI_TEMPLATE(class SD)
(requires //
requires(std::declval<SD&>().schedule())) //
auto schedule(SD& sd) //
noexcept(noexcept(sd.schedule())) {
return sd.schedule();
}
PUSHMI_TEMPLATE(class SD, class TP)
(requires //
requires(std::declval<SD&>().schedule(
std::declval<TP (&)(TP)>()(top(std::declval<SD&>()))))) //
auto schedule(SD& sd, TP tp) //
noexcept(noexcept(sd.schedule(std::move(tp)))) {
return sd.schedule(std::move(tp));
}
//
......@@ -99,67 +114,94 @@ PUSHMI_TEMPLATE(class SD, class TP, class Out)
//
PUSHMI_TEMPLATE(class S)
(requires requires( //
std::declval<S&>()->done())) //
void set_done(S& s) //
noexcept(noexcept(s->done())) {
s->done();
(requires //
requires(set_done(*std::declval<S>()))) //
void set_done(S&& s) //
noexcept(noexcept(set_done(*s))) {
set_done(*s);
}
PUSHMI_TEMPLATE(class S, class E)
(requires requires( //
std::declval<S&>()->error(std::declval<E>()))) //
void set_error(S& s, E e) //
noexcept(noexcept(s->error(std::move(e)))) {
s->error(std::move(e));
(requires //
requires(set_error(*std::declval<S>(), std::declval<E>()))) //
void set_error(S&& s, E e) //
noexcept(noexcept(set_error(*s, std::move(e)))) {
set_error(*s, std::move(e));
}
PUSHMI_TEMPLATE(class S, class... VN)
(requires requires( //
std::declval<S&>()->value(std::declval<VN&&>()...))) //
void set_value(S& s, VN&&... vn) //
noexcept(noexcept(s->value((VN &&) vn...))) {
s->value((VN &&) vn...);
(requires //
requires(set_value(*std::declval<S>(), std::declval<VN>()...))) //
void set_value(S&& s, VN&&... vn) //
noexcept(noexcept(set_value(*s, (VN &&) vn...))) {
set_value(*s, (VN &&) vn...);
}
PUSHMI_TEMPLATE(class S, class Up)
(requires requires( //
std::declval<S&>()->starting(std::declval<Up&&>()))) //
void set_starting(S& s, Up&& up) //
noexcept(noexcept(s->starting((Up &&) up))) {
s->starting((Up &&) up);
(requires //
requires(set_starting(*std::declval<S>(), std::declval<Up>()))) //
void set_starting(S&& s, Up&& up) //
noexcept(noexcept(set_starting(*s, (Up &&) up))) {
set_starting(*s, (Up &&) up);
}
PUSHMI_TEMPLATE(class SD)
(requires requires( //
std::declval<SD&>()->executor())) //
auto executor(SD& sd) //
noexcept(noexcept(sd->executor())) {
return sd->executor();
(requires //
requires(get_executor(*std::declval<SD>()))) //
auto get_executor(SD&& sd) //
noexcept(noexcept(get_executor(*sd))) {
return get_executor(*sd);
}
PUSHMI_TEMPLATE(class SD)
(requires //
requires(make_strand(*std::declval<SD>()))) //
auto make_strand(SD&& sd) //
noexcept(noexcept(make_strand(*sd))) {
return make_strand(*sd);
}
PUSHMI_TEMPLATE(class SD, class Out)
(requires requires( //
std::declval<SD&>()->submit(std::declval<Out>()))) //
void submit(SD& sd, Out out) //
noexcept(noexcept(sd->submit(std::move(out)))) {
sd->submit(std::move(out));
(requires //
requires(submit(*std::declval<SD>(), std::declval<Out>()))) //
void submit(SD&& sd, Out&& out) //
noexcept(noexcept(submit(*sd, (Out &&) out))) {
submit(*sd, (Out &&) out);
}
PUSHMI_TEMPLATE(class SD)
(requires requires( //
std::declval<SD&>()->top())) //
auto top(SD& sd) //
noexcept(noexcept(sd->top())) {
return sd->top();
(requires //
requires(top(*std::declval<SD>()))) //
auto top(SD&& sd) //
noexcept(noexcept(top(*sd))) {
return top(*sd);
}
PUSHMI_TEMPLATE(class SD, class TP, class Out)
(requires requires( //
std::declval<SD&>()->submit(
std::declval<TP (&)(TP)>()(top(std::declval<SD&>())),
std::declval<Out>()))) //
void submit(SD& sd, TP tp, Out out) //
noexcept(noexcept(sd->submit(std::move(tp), std::move(out)))) {
sd->submit(std::move(tp), std::move(out));
PUSHMI_TEMPLATE(class SD)
(requires //
requires(schedule(*std::declval<SD>()))) //
auto schedule(SD&& sd) //
noexcept(noexcept(schedule(*sd))) {
return schedule(*sd);
}
PUSHMI_TEMPLATE(class SD, class TP)
(requires //
requires(schedule(
*std::declval<SD>(),
std::declval<TP (&)(TP)>()(top(std::declval<SD&>()))))) //
auto schedule(SD&& sd, TP tp) //
noexcept(noexcept(schedule(*sd, std::move(tp)))) {
return schedule(*sd, std::move(tp));
}
//
// support a nullary function as a StrandFactory
//
PUSHMI_TEMPLATE(class S)
(requires Invocable<S&>) //
auto make_strand(S& s) //
noexcept(noexcept(s())) {
return s();
}
//
......@@ -172,7 +214,7 @@ PUSHMI_TEMPLATE(class S)
PUSHMI_TEMPLATE(class S, class E)
(requires Invocable<S&>) //
void set_error(S&, E&&) noexcept {
std::abort();
std::terminate();
}
PUSHMI_TEMPLATE(class S)
(requires Invocable<S&>) //
......@@ -195,94 +237,33 @@ void set_error(std::promise<T>& p, std::exception_ptr e) noexcept {
p.set_exception(std::move(e));
}
template <class T, class E>
void set_error(std::promise<T>& p, E e) noexcept {
void set_error(std::promise<T>& p, E e) //
noexcept {
p.set_exception(std::make_exception_ptr(std::move(e)));
}
template <class T>
void set_value(std::promise<T>& p, T t) noexcept(
noexcept(p.set_value(std::move(t)))) {
p.set_value(std::move(t));
PUSHMI_TEMPLATE(class T, class U)
(requires ConvertibleTo<U, T>) //
void set_value(std::promise<T>& p, U&& u) //
noexcept(noexcept(p.set_value((U &&) u))) {
p.set_value((U &&) u);
}
inline void set_value(std::promise<void>& p) noexcept(noexcept(p.set_value())) {
inline void set_value(std::promise<void>& p) //
noexcept(noexcept(p.set_value())) {
p.set_value();
}
//
// support reference_wrapper
//
PUSHMI_TEMPLATE(class S)
(requires requires( //
set_done(std::declval<S&>()))) //
void set_done(std::reference_wrapper<S> s) noexcept(
noexcept(set_done(s.get()))) {
set_done(s.get());
}
PUSHMI_TEMPLATE(class S, class E)
(requires requires( //
set_error(std::declval<S&>(), std::declval<E>()))) //
void set_error(std::reference_wrapper<S> s, E e) noexcept {
set_error(s.get(), std::move(e));
}
PUSHMI_TEMPLATE(class S, class... VN)
(requires requires( //
set_value(std::declval<S&>(), std::declval<VN&&>()...))) //
void set_value(std::reference_wrapper<S> s, VN&&... vn) //
noexcept(noexcept(set_value(s.get(), (VN &&) vn...))) {
set_value(s.get(), (VN &&) vn...);
}
PUSHMI_TEMPLATE(class S, class Up)
(requires requires( //
set_starting(std::declval<S&>(), std::declval<Up&&>()))) //
void set_starting(std::reference_wrapper<S> s, Up&& up) //
noexcept(noexcept(set_starting(s.get(), (Up &&) up))) {
set_starting(s.get(), (Up &&) up);
}
PUSHMI_TEMPLATE(class SD)
(requires requires( //
executor(std::declval<SD&>()))) //
auto executor(std::reference_wrapper<SD> sd) //
noexcept(noexcept(executor(sd.get()))) {
return executor(sd.get());
}
PUSHMI_TEMPLATE(class SD, class Out)
(requires requires( //
submit(std::declval<SD&>(), std::declval<Out>()))) //
void submit(std::reference_wrapper<SD> sd, Out out) //
noexcept(noexcept(submit(sd.get(), std::move(out)))) {
submit(sd.get(), std::move(out));
}
PUSHMI_TEMPLATE(class SD)
(requires requires( //
top(std::declval<SD&>()))) //
auto top(std::reference_wrapper<SD> sd) //
noexcept(noexcept(top(sd.get()))) {
return top(sd.get());
}
PUSHMI_TEMPLATE(class SD, class TP, class Out)
(requires requires( //
submit(
std::declval<SD&>(),
std::declval<TP (&)(TP)>()(top(std::declval<SD&>())),
std::declval<Out>()))) //
void submit(std::reference_wrapper<SD> sd, TP tp, Out out) //
noexcept(noexcept(submit(sd.get(), std::move(tp), std::move(out)))) {
submit(sd.get(), std::move(tp), std::move(out));
}
//
// accessors for free functions in this namespace
//
struct set_done_fn {
PUSHMI_TEMPLATE(class S)
(requires requires( //
set_done(std::declval<S&>()),
set_error(std::declval<S&>(), std::current_exception()))) //
(requires //
requires(
set_done(std::declval<S&>()),
set_error(std::declval<S&>(), std::current_exception()))) //
void
operator()(S&& s) const //
noexcept(noexcept(set_done(s))) {
operator()(S& s) const noexcept {
try {
set_done(s);
} catch (...) {
......@@ -292,22 +273,22 @@ struct set_done_fn {
};
struct set_error_fn {
PUSHMI_TEMPLATE(class S, class E)
(requires requires( //
set_error(std::declval<S&>(), std::declval<E>()))) //
(requires //
requires(set_error(std::declval<S&>(), std::declval<E>()))) //
void
operator()(S&& s, E e) const //
noexcept(noexcept(set_error(s, std::move(e)))) {
set_error(s, std::move(e));
operator()(S& s, E&& e) const //
noexcept(noexcept(set_error(s, (E &&)e))) {
set_error(s, (E &&)e);
}
};
struct set_value_fn {
PUSHMI_TEMPLATE(class S, class... VN)
(requires requires( //
set_value(std::declval<S&>(), std::declval<VN&&>()...),
set_error(std::declval<S&>(), std::current_exception()))) //
(requires //
requires(
set_value(std::declval<S&>(), std::declval<VN>()...),
set_error(std::declval<S&>(), std::current_exception()))) //
void
operator()(S&& s, VN&&... vn) const //
noexcept(noexcept(set_value(s, (VN &&) vn...))) {
operator()(S& s, VN&&... vn) const noexcept {
try {
set_value(s, (VN &&) vn...);
} catch (...) {
......@@ -318,12 +299,12 @@ struct set_value_fn {
struct set_starting_fn {
PUSHMI_TEMPLATE(class S, class Up)
(requires requires( //
set_starting(std::declval<S&>(), std::declval<Up&&>()),
set_error(std::declval<S&>(), std::current_exception()))) //
(requires //
requires(
set_starting(std::declval<S&>(), std::declval<Up>()),
set_error(std::declval<S&>(), std::current_exception()))) //
void
operator()(S&& s, Up&& up) const //
noexcept(noexcept(set_starting(s, (Up &&) up))) {
operator()(S& s, Up&& up) const noexcept {
try {
set_starting(s, (Up &&) up);
} catch (...) {
......@@ -334,56 +315,54 @@ struct set_starting_fn {
struct get_executor_fn {
PUSHMI_TEMPLATE(class SD)
(requires requires( //
executor(std::declval<SD&>()))) //
(requires //
requires(get_executor(std::declval<SD&>()))) //
auto
operator()(SD&& sd) const //
noexcept(noexcept(executor(sd))) {
return executor(sd);
operator()(SD& sd) const //
noexcept(noexcept(get_executor(sd))) {
return get_executor(sd);
}
};
struct do_submit_fn {
PUSHMI_TEMPLATE(class SD, class Out)
(requires requires( //
submit(std::declval<SD&>(), std::declval<Out>()))) //
void
operator()(SD&& s, Out out) const //
noexcept(noexcept(submit(s, std::move(out)))) {
submit(s, std::move(out));
struct make_strand_fn {
PUSHMI_TEMPLATE(class SD)
(requires //
requires(make_strand(std::declval<SD&>()))) //
auto
operator()(SD& sd) const //
noexcept(noexcept(make_strand(sd))) {
return make_strand(sd);
}
};
struct do_submit_fn {
PUSHMI_TEMPLATE(class SD, class Out)
(requires requires( //
submit(
std::declval<SD&>(),
top(std::declval<SD&>()),
std::declval<Out>()))) //
(requires //
requires(submit(std::declval<SD>(), std::declval<Out>()))) //
void
operator()(SD&& s, Out out) const //
noexcept(noexcept(submit(s, top(s), std::move(out)))) {
submit(s, top(s), std::move(out));
operator()(SD&& s, Out&& out) const //
noexcept(noexcept(submit((SD &&) s, (Out &&) out))) {
submit((SD &&) s, (Out &&) out);
}
};
PUSHMI_TEMPLATE(class SD, class TP, class Out)
(requires requires( //
submit(
std::declval<SD&>(),
std::declval<TP>(),
std::declval<Out>()))) //
void
operator()(SD&& s, TP tp, Out out) const //
noexcept(noexcept(submit(s, std::move(tp), std::move(out)))) {
submit(s, std::move(tp), std::move(out));
struct do_schedule_fn {
PUSHMI_TEMPLATE(class SD, class... VN)
(requires //
requires(schedule(std::declval<SD&>(), std::declval<VN>()...))) //
auto
operator()(SD& s, VN&&... vn) const //
noexcept(noexcept(schedule(s, (VN &&) vn...))) {
return schedule(s, (VN &&) vn...);
}
};
struct get_top_fn {
PUSHMI_TEMPLATE(class SD)
(requires requires( //
top(std::declval<SD&>()))) //
(requires //
requires(top(std::declval<SD&>()))) //
auto
operator()(SD&& sd) const //
operator()(SD& sd) const //
noexcept(noexcept(top(sd))) {
return top(sd);
}
......@@ -395,11 +374,16 @@ PUSHMI_INLINE_VAR constexpr __adl::set_done_fn set_done{};
PUSHMI_INLINE_VAR constexpr __adl::set_error_fn set_error{};
PUSHMI_INLINE_VAR constexpr __adl::set_value_fn set_value{};
PUSHMI_INLINE_VAR constexpr __adl::set_starting_fn set_starting{};
PUSHMI_INLINE_VAR constexpr __adl::get_executor_fn executor{};
PUSHMI_INLINE_VAR constexpr __adl::get_executor_fn get_executor{};
PUSHMI_INLINE_VAR constexpr __adl::make_strand_fn make_strand{};
PUSHMI_INLINE_VAR constexpr __adl::do_submit_fn submit{};
PUSHMI_INLINE_VAR constexpr __adl::do_schedule_fn schedule{};
PUSHMI_INLINE_VAR constexpr __adl::get_top_fn now{};
PUSHMI_INLINE_VAR constexpr __adl::get_top_fn top{};
template <class T>
struct property_set_traits<T*> : property_set_traits<T> {};
template <class T>
struct property_set_traits<
T,
......
......@@ -18,6 +18,7 @@
#include <folly/experimental/pushmi/flow_receiver.h>
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <type_traits>
namespace folly {
namespace pushmi {
......@@ -26,7 +27,7 @@ template <class PE, class PV, class E, class... VN>
class any_flow_many_sender {
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::tuple<VN...>)]; // can hold a V in-situ
std::aligned_union_t<0, std::tuple<VN...>> buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
......@@ -35,10 +36,8 @@ class any_flow_many_sender {
}
struct vtable {
static void s_op(data&, data*) {}
static any_executor<E> s_executor(data&) { return {}; }
static void s_submit(data&, any_flow_receiver<PE, PV, E, VN...>) {}
void (*op_)(data&, data*) = vtable::s_op;
any_executor<E> (*executor_)(data&) = vtable::s_executor;
void (*submit_)(data&, any_flow_receiver<PE, PV, E, VN...>) = vtable::s_submit;
};
static constexpr vtable const noop_ {};
......@@ -51,16 +50,12 @@ class any_flow_many_sender {
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static any_executor<E> executor(data& src) {
return any_executor<E>{
::folly::pushmi::executor(*static_cast<Wrapped*>(src.pobj_))};
}
static void submit(data& src, any_flow_receiver<PE, PV, E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>(src.pobj_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
static const vtable vtbl{s::op, s::submit};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
......@@ -70,21 +65,17 @@ class any_flow_many_sender {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_) Wrapped(
std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static any_executor<E> executor(data& src) {
return any_executor<E>{::folly::pushmi::executor(
*static_cast<Wrapped*>((void*)src.buffer_))};
new (&dst->buffer_) Wrapped(
std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static void submit(data& src, any_flow_receiver<PE, PV, E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>((void*)src.buffer_), std::move(out));
*static_cast<Wrapped*>((void*)&src.buffer_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
new (data_.buffer_) Wrapped(std::move(obj));
static const vtable vtbl{s::op, s::submit};
new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
......@@ -111,11 +102,10 @@ class any_flow_many_sender {
new ((void*)this) any_flow_many_sender(std::move(that));
return *this;
}
any_executor<E> executor() {
return vptr_->executor_(data_);
}
void submit(any_flow_receiver<PE, PV, E, VN...> out) {
vptr_->submit_(data_, std::move(out));
PUSHMI_TEMPLATE(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) //
void submit(Out&& out) {
vptr_->submit_(data_, any_flow_receiver<PE, PV, E, VN...>{(Out &&) out});
}
};
......@@ -124,10 +114,9 @@ template <class PE, class PV, class E, class... VN>
constexpr typename any_flow_many_sender<PE, PV, E, VN...>::vtable const
any_flow_many_sender<PE, PV, E, VN...>::noop_;
template <class SF, class EXF>
class flow_many_sender<SF, EXF> {
template <class SF>
class flow_many_sender<SF> {
SF sf_;
EXF exf_;
public:
using properties = property_set<is_sender<>, is_flow<>, is_many<>>;
......@@ -135,10 +124,7 @@ class flow_many_sender<SF, EXF> {
constexpr flow_many_sender() = default;
constexpr explicit flow_many_sender(SF sf)
: sf_(std::move(sf)) {}
constexpr flow_many_sender(SF sf, EXF exf)
: sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() { return exf_(); }
PUSHMI_TEMPLATE(class Out)
(requires FlowReceiver<Out> && Invocable<SF&, Out>)
void submit(Out out) {
......@@ -146,11 +132,10 @@ class flow_many_sender<SF, EXF> {
}
};
template <PUSHMI_TYPE_CONSTRAINT(Sender<is_many<>, is_flow<>>) Data, class DSF, class DEXF>
class flow_many_sender<Data, DSF, DEXF> {
template <PUSHMI_TYPE_CONSTRAINT(Sender<is_many<>, is_flow<>>) Data, class DSF>
class flow_many_sender<Data, DSF> {
Data data_;
DSF sf_;
DEXF exf_;
public:
using properties = property_set_insert_t<properties_t<Data>, property_set<is_sender<>, is_flow<>, is_many<>>>;
......@@ -160,10 +145,7 @@ class flow_many_sender<Data, DSF, DEXF> {
: data_(std::move(data)) {}
constexpr flow_many_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
constexpr flow_many_sender(Data data, DSF sf, DEXF exf)
: data_(std::move(data)), sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() { return exf_(data_); }
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(lazy::FlowReceiver<Out> PUSHMI_AND
lazy::Invocable<DSF&, Data&, Out>))
......@@ -174,7 +156,7 @@ class flow_many_sender<Data, DSF, DEXF> {
template <>
class flow_many_sender<>
: public flow_many_sender<ignoreSF, trampolineEXF> {
: public flow_many_sender<ignoreSF> {
public:
flow_many_sender() = default;
};
......@@ -183,59 +165,41 @@ public:
// make_flow_many_sender
PUSHMI_INLINE_VAR constexpr struct make_flow_many_sender_fn {
inline auto operator()() const {
return flow_many_sender<ignoreSF, trampolineEXF>{};
return flow_many_sender<ignoreSF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf) const {
return flow_many_sender<SF, trampolineEXF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<> && Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf, EXF exf) const {
return flow_many_sender<SF, EXF>{std::move(sf), std::move(exf)};
return flow_many_sender<SF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<> && Sender<Data, is_many<>, is_flow<>>)
auto operator()(Data d) const {
return flow_many_sender<Data, passDSF, passDEXF>{std::move(d)};
return flow_many_sender<Data, passDSF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_many<>, is_flow<>>)
auto operator()(Data d, DSF sf) const {
return flow_many_sender<Data, DSF, passDEXF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_many<>, is_flow<>> && Invocable<DEXF&, Data&>)
auto operator()(Data d, DSF sf, DEXF exf) const {
return flow_many_sender<Data, DSF, DEXF>{std::move(d), std::move(sf), std::move(exf)};
return flow_many_sender<Data, DSF>{std::move(d), std::move(sf)};
}
} const make_flow_many_sender {};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
flow_many_sender() -> flow_many_sender<ignoreSF, trampolineEXF>;
flow_many_sender() -> flow_many_sender<ignoreSF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
flow_many_sender(SF) -> flow_many_sender<SF, trampolineEXF>;
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<> && Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
flow_many_sender(SF, EXF) -> flow_many_sender<SF, EXF>;
flow_many_sender(SF) -> flow_many_sender<SF>;
PUSHMI_TEMPLATE(class Data)
(requires True<> && Sender<Data, is_many<>, is_flow<>>)
flow_many_sender(Data) -> flow_many_sender<Data, passDSF, passDEXF>;
flow_many_sender(Data) -> flow_many_sender<Data, passDSF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_many<>, is_flow<>>)
flow_many_sender(Data, DSF) -> flow_many_sender<Data, DSF, passDEXF>;
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_many<>, is_flow<>> && Invocable<DEXF&, Data&>)
flow_many_sender(Data, DSF, DEXF) -> flow_many_sender<Data, DSF, DEXF>;
flow_many_sender(Data, DSF) -> flow_many_sender<Data, DSF>;
#endif
template<>
......
......@@ -16,6 +16,7 @@
#pragma once
#include <folly/experimental/pushmi/receiver.h>
#include <type_traits>
namespace folly {
namespace pushmi {
......@@ -26,7 +27,7 @@ class any_flow_receiver {
bool started_ = false;
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::tuple<VN...>)]; // can hold V in-situ
std::aligned_union_t<0, std::tuple<VN...>> buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
......@@ -77,25 +78,25 @@ class any_flow_receiver {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_) Wrapped(
std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
new (&dst->buffer_) Wrapped(
std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static void done(data& src) {
set_done(*static_cast<Wrapped*>((void*)src.buffer_));
set_done(*static_cast<Wrapped*>((void*)&src.buffer_));
}
static void error(data& src, E e) noexcept {
set_error(*static_cast<Wrapped*>((void*)src.buffer_), std::move(e));
set_error(*static_cast<Wrapped*>((void*)&src.buffer_), std::move(e));
}
static void value(data& src, VN... vn) {
set_value(*static_cast<Wrapped*>((void*)src.buffer_), std::move(vn)...);
set_value(*static_cast<Wrapped*>((void*)&src.buffer_), std::move(vn)...);
}
static void starting(data& src, any_receiver<PE, PV> up) {
set_starting(*static_cast<Wrapped*>((void*)src.buffer_), std::move(up));
set_starting(*static_cast<Wrapped*>((void*)&src.buffer_), std::move(up));
}
};
static const vtable vtbl{s::op, s::done, s::error, s::value, s::starting};
new (data_.buffer_) Wrapped(std::move(obj));
new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
......@@ -128,11 +129,12 @@ public:
if (done_){ return; }
vptr_->value_(data_, std::move(vn)...);
}
void error(E e) noexcept {
template<class A>
void error(A&& e) noexcept {
if (!started_) {std::terminate();}
if (done_){ return; }
done_ = true;
vptr_->error_(data_, std::move(e));
vptr_->error_(data_, E{(A&&)e});
}
void done() {
if (!started_) {std::terminate();}
......@@ -141,10 +143,12 @@ public:
vptr_->done_(data_);
}
void starting(any_receiver<PE, PV> up) {
PUSHMI_TEMPLATE(class Up)
(requires ReceiveValue<Up, PV> && ReceiveError<Up, PE>)
void starting(Up&& up) {
if (started_) {std::terminate();}
started_ = true;
vptr_->starting_(data_, std::move(up));
vptr_->starting_(data_, any_receiver<PE, PV>{(Up&&)up});
}
};
......@@ -194,20 +198,20 @@ class flow_receiver<VF, EF, DF, StrtF> {
df_(std::move(df)),
strtf_(std::move(strtf)) {}
PUSHMI_TEMPLATE (class V)
(requires Invocable<VF&, V>)
(requires Invocable<VF&, V>) //
void value(V&& v) {
if (!started_) {std::terminate();}
if (done_){ return; }
nf_((V&&) v);
}
PUSHMI_TEMPLATE (class E)
(requires Invocable<EF&, E>)
void error(E e) noexcept {
static_assert(NothrowInvocable<EF&, E>, "error function must be noexcept");
(requires Invocable<EF&, E&&>) //
void error(E&& e) noexcept {
static_assert(NothrowInvocable<EF&, E&&>, "error function must be noexcept");
if (!started_) {std::terminate();}
if (done_){ return; }
done_ = true;
ef_(std::move(e));
ef_((E&&)e);
}
void done() {
if (!started_) {std::terminate();}
......@@ -273,25 +277,27 @@ class flow_receiver<Data, DVF, DEF, DDF, DStrtF> {
Data& data() { return data_; }
PUSHMI_TEMPLATE (class V)
(requires Invocable<DVF&, Data&, V>)
void value(V&& v) {
PUSHMI_TEMPLATE (class... VN)
(requires Invocable<DVF&, Data&, VN...>) //
void value(VN&&... vn) {
if (!started_) {std::terminate();}
if (done_){ return; }
nf_(data_, (V&&) v);
nf_(data_, (VN&&) vn...);
}
PUSHMI_TEMPLATE (class E)
(requires Invocable<DEF&, Data&, E>)
(requires Invocable<DEF&, Data&, E&&>) //
void error(E&& e) noexcept {
static_assert(
NothrowInvocable<DEF&, Data&, E>, "error function must be noexcept");
NothrowInvocable<DEF&, Data&, E&&>, "error function must be noexcept");
if (!started_) {std::terminate();}
if (done_){ return; }
done_ = true;
ef_(data_, (E&&) e);
}
void done() {
if (!started_) {std::terminate();}
if (!started_) {
std::terminate();
}
if (done_){ return; }
done_ = true;
df_(data_);
......@@ -340,9 +346,9 @@ PUSHMI_INLINE_VAR constexpr struct make_flow_receiver_fn {
return flow_receiver<ignoreVF, on_error_fn<EFN...>, ignoreDF, ignoreStrtF>{
std::move(ef)};
}
template <class... DFN>
auto operator()(on_done_fn<DFN...> df) const {
return flow_receiver<ignoreVF, abortEF, on_done_fn<DFN...>, ignoreStrtF>{
template <class DF>
auto operator()(on_done_fn<DF> df) const {
return flow_receiver<ignoreVF, abortEF, on_done_fn<DF>, ignoreStrtF>{
std::move(df)};
}
PUSHMI_TEMPLATE (class VF, class EF)
......@@ -405,11 +411,11 @@ PUSHMI_INLINE_VAR constexpr struct make_flow_receiver_fn {
return flow_receiver<Data, passDVF, on_error_fn<DEFN...>, passDDF, passDStrtF>{
std::move(d), std::move(ef)};
}
PUSHMI_TEMPLATE(class Data, class... DDFN)
PUSHMI_TEMPLATE(class Data, class DDF)
(requires PUSHMI_EXP(
lazy::FlowReceiverDataArg<Data>))
auto operator()(Data d, on_done_fn<DDFN...> df) const {
return flow_receiver<Data, passDVF, passDEF, on_done_fn<DDFN...>, passDStrtF>{
auto operator()(Data d, on_done_fn<DDF> df) const {
return flow_receiver<Data, passDVF, passDEF, on_done_fn<DDF>, passDStrtF>{
std::move(d), std::move(df)};
}
PUSHMI_TEMPLATE(class Data, class DVF, class DEF)
......@@ -463,9 +469,9 @@ template <class... EFN>
flow_receiver(on_error_fn<EFN...>) ->
flow_receiver<ignoreVF, on_error_fn<EFN...>, ignoreDF, ignoreStrtF>;
template <class... DFN>
flow_receiver(on_done_fn<DFN...>) ->
flow_receiver<ignoreVF, abortEF, on_done_fn<DFN...>, ignoreStrtF>;
template <class DF>
flow_receiver(on_done_fn<DF>) ->
flow_receiver<ignoreVF, abortEF, on_done_fn<DF>, ignoreStrtF>;
PUSHMI_TEMPLATE(class VF, class EF)
(requires PUSHMI_EXP(
......@@ -521,19 +527,11 @@ PUSHMI_TEMPLATE(class Data, class... DEFN)
flow_receiver(Data d, on_error_fn<DEFN...>) ->
flow_receiver<Data, passDVF, on_error_fn<DEFN...>, passDDF, passDStrtF>;
PUSHMI_TEMPLATE(class Data, class... DDFN)
(requires PUSHMI_EXP(
lazy::FlowReceiverDataArg<Data>))
flow_receiver(Data d, on_done_fn<DDFN...>) ->
flow_receiver<Data, passDVF, passDEF, on_done_fn<DDFN...>, passDStrtF>;
PUSHMI_TEMPLATE(class Data, class DDF)
(requires PUSHMI_EXP(
lazy::True<> PUSHMI_AND
lazy::FlowReceiverDataArg<Data> PUSHMI_AND
lazy::Invocable<DDF&, Data&>))
flow_receiver(Data d, DDF) ->
flow_receiver<Data, passDVF, passDEF, DDF, passDStrtF>;
lazy::FlowReceiverDataArg<Data>))
flow_receiver(Data d, on_done_fn<DDF>) ->
flow_receiver<Data, passDVF, passDEF, on_done_fn<DDF>, passDStrtF>;
PUSHMI_TEMPLATE(class Data, class DVF, class DEF)
(requires PUSHMI_EXP(
......
......@@ -18,6 +18,7 @@
#include <folly/experimental/pushmi/flow_receiver.h>
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <type_traits>
namespace folly {
namespace pushmi {
......@@ -26,7 +27,8 @@ template <class PE, class E, class... VN>
class any_flow_single_sender {
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::tuple<VN...>)]; // can hold a V in-situ
std::aligned_storage_t<
sizeof(std::tuple<VN...>), alignof(std::tuple<VN...>)> buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
......@@ -35,10 +37,8 @@ class any_flow_single_sender {
}
struct vtable {
static void s_op(data&, data*) {}
static any_executor<E> s_executor(data&) { return {}; }
static void s_submit(data&, any_flow_receiver<PE, std::ptrdiff_t, E, VN...>) {}
void (*op_)(data&, data*) = vtable::s_op;
any_executor<E> (*executor_)(data&) = vtable::s_executor;
void (*submit_)(data&, any_flow_receiver<PE, std::ptrdiff_t, E, VN...>) =
vtable::s_submit;
};
......@@ -53,10 +53,6 @@ class any_flow_single_sender {
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static any_executor<E> executor(data& src) {
return any_executor<E>{
::folly::pushmi::executor(*static_cast<Wrapped*>(src.pobj_))};
}
static void submit(
data& src,
any_flow_receiver<PE, std::ptrdiff_t, E, VN...> out) {
......@@ -64,7 +60,7 @@ class any_flow_single_sender {
*static_cast<Wrapped*>(src.pobj_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
static const vtable vtbl{s::op, s::submit};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
......@@ -74,23 +70,19 @@ class any_flow_single_sender {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static any_executor<E> executor(data& src) {
return any_executor<E>{::folly::pushmi::executor(
*static_cast<Wrapped*>((void*)src.buffer_))};
new (&dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static void submit(
data& src,
any_flow_receiver<PE, std::ptrdiff_t, E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>((void*)src.buffer_), std::move(out));
*static_cast<Wrapped*>((void*)&src.buffer_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
new (data_.buffer_) Wrapped(std::move(obj));
static const vtable vtbl{s::op, s::submit};
new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
......@@ -117,11 +109,10 @@ class any_flow_single_sender {
new ((void*)this) any_flow_single_sender(std::move(that));
return *this;
}
any_executor<E> executor() {
return vptr_->executor_(data_);
}
void submit(any_flow_receiver<PE, std::ptrdiff_t, E, VN...> out) {
vptr_->submit_(data_, std::move(out));
PUSHMI_TEMPLATE(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) //
void submit(Out&& out) {
vptr_->submit_(data_, any_flow_receiver<PE, std::ptrdiff_t, E, VN...>{(Out &&) out});
}
};
......@@ -130,10 +121,9 @@ template <class PE, class E, class... VN>
constexpr typename any_flow_single_sender<PE, E, VN...>::vtable const
any_flow_single_sender<PE, E, VN...>::noop_;
template <class SF, class EXF>
class flow_single_sender<SF, EXF> {
template <class SF>
class flow_single_sender<SF> {
SF sf_;
EXF exf_;
public:
using properties = property_set<is_sender<>, is_flow<>, is_single<>>;
......@@ -141,10 +131,7 @@ class flow_single_sender<SF, EXF> {
constexpr flow_single_sender() = default;
constexpr explicit flow_single_sender(SF sf)
: sf_(std::move(sf)) {}
constexpr flow_single_sender(SF sf, EXF exf)
: sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() { return exf_(); }
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out> && Invocable<SF&, Out>)
void submit(Out out) {
......@@ -154,12 +141,10 @@ class flow_single_sender<SF, EXF> {
template <
PUSHMI_TYPE_CONSTRAINT(Sender<is_single<>, is_flow<>>) Data,
class DSF,
class DEXF>
class flow_single_sender<Data, DSF, DEXF> {
class DSF>
class flow_single_sender<Data, DSF> {
Data data_;
DSF sf_;
DEXF exf_;
public:
using properties = property_set_insert_t<
......@@ -171,10 +156,7 @@ class flow_single_sender<Data, DSF, DEXF> {
: data_(std::move(data)) {}
constexpr flow_single_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
constexpr flow_single_sender(Data data, DSF sf, DEXF exf)
: data_(std::move(data)), sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() { return exf_(data_); }
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(lazy::Receiver<Out> PUSHMI_AND
lazy::Invocable<DSF&, Data&, Out>))
......@@ -185,7 +167,7 @@ class flow_single_sender<Data, DSF, DEXF> {
template <>
class flow_single_sender<>
: public flow_single_sender<ignoreSF, trampolineEXF> {
: public flow_single_sender<ignoreSF> {
public:
flow_single_sender() = default;
};
......@@ -194,62 +176,41 @@ public:
// make_flow_single_sender
PUSHMI_INLINE_VAR constexpr struct make_flow_single_sender_fn {
inline auto operator()() const {
return flow_single_sender<ignoreSF, trampolineEXF>{};
return flow_single_sender<ignoreSF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf) const {
return flow_single_sender<SF, trampolineEXF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<> && Invocable<EXF&>
PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf, EXF exf) const {
return flow_single_sender<SF, EXF>{std::move(sf), std::move(exf)};
return flow_single_sender<SF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<> && Sender<Data, is_single<>, is_flow<>>)
auto operator()(Data d) const {
return flow_single_sender<Data, passDSF, passDEXF>{std::move(d)};
return flow_single_sender<Data, passDSF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_single<>, is_flow<>>)
auto operator()(Data d, DSF sf) const {
return flow_single_sender<Data, DSF, passDEXF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_single<>, is_flow<>> && Invocable<DEXF&, Data&>)
auto operator()(Data d, DSF sf, DEXF exf) const {
return flow_single_sender<Data, DSF, DEXF>{std::move(d),
std::move(sf), std::move(exf)};
return flow_single_sender<Data, DSF>{std::move(d), std::move(sf)};
}
} const make_flow_single_sender {};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
flow_single_sender() -> flow_single_sender<ignoreSF, trampolineEXF>;
flow_single_sender() -> flow_single_sender<ignoreSF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
flow_single_sender(SF) -> flow_single_sender<SF, trampolineEXF>;
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<> && Invocable<EXF&>
PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
flow_single_sender(SF, EXF) -> flow_single_sender<SF, EXF>;
flow_single_sender(SF) -> flow_single_sender<SF>;
PUSHMI_TEMPLATE(class Data)
(requires True<> && Sender<Data, is_single<>, is_flow<>>)
flow_single_sender(Data) -> flow_single_sender<Data, passDSF, passDEXF>;
flow_single_sender(Data) -> flow_single_sender<Data, passDSF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_single<>, is_flow<>>)
flow_single_sender(Data, DSF) -> flow_single_sender<Data, DSF, passDEXF>;
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_single<>, is_flow<>> && Invocable<DEXF&, Data&>)
flow_single_sender(Data, DSF, DEXF) -> flow_single_sender<Data, DSF, DEXF>;
flow_single_sender(Data, DSF) -> flow_single_sender<Data, DSF>;
#endif
template<>
......
......@@ -74,22 +74,25 @@ struct is_concurrent_sequence;
// implementation types
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class receiver;
class executor;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class flow_receiver;
class constrained_executor;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class single_sender;
class time_executor;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class many_sender;
class receiver;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class constrained_single_sender;
class flow_receiver;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class time_single_sender;
class single_sender;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class many_sender;
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class flow_single_sender;
......@@ -123,35 +126,28 @@ template <
class... VN>
class any_flow_many_sender;
template <class E = std::exception_ptr, class C = std::ptrdiff_t, class... VN>
class any_constrained_single_sender;
template <
class E = std::exception_ptr,
class TP = std::chrono::system_clock::time_point,
class... VN>
class any_time_single_sender;
template <class E = std::exception_ptr>
struct any_executor;
template <class E = std::exception_ptr, class... VN>
class any_executor;
template <class E = std::exception_ptr>
template <class E = std::exception_ptr, class... VN>
struct any_executor_ref;
template <class E = std::exception_ptr, class CV = std::ptrdiff_t>
struct any_constrained_executor;
template <class E = std::exception_ptr, class CV = std::ptrdiff_t, class... VN>
class any_constrained_executor;
template <class E = std::exception_ptr, class TP = std::ptrdiff_t>
template <class E = std::exception_ptr, class TP = std::ptrdiff_t, class... VN>
struct any_constrained_executor_ref;
template <
class E = std::exception_ptr,
class TP = std::chrono::system_clock::time_point>
struct any_time_executor;
class TP = std::chrono::system_clock::time_point,
class... VN>
class any_time_executor;
template <
class E = std::exception_ptr,
class TP = std::chrono::system_clock::time_point>
class TP = std::chrono::system_clock::time_point,
class... VN>
struct any_time_executor_ref;
namespace operators {}
......
......@@ -22,23 +22,34 @@ namespace pushmi {
class inline_constrained_executor_t {
public:
using properties = property_set<
is_constrained<>,
is_fifo_sequence<>>;
template<class CV>
struct task {
CV cv_;
using properties = property_set<
is_constrained<>,
is_executor<>,
is_sender<>,
is_always_blocking<>,
is_fifo_sequence<>,
is_single<>>;
std::ptrdiff_t top() {
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>) //
void submit(Out out) && {
set_value(out, inline_constrained_executor_t{});
set_done(out);
}
};
auto top() {
return 0;
}
auto executor() {
return *this;
task<std::ptrdiff_t> schedule() {
return {top()};
}
PUSHMI_TEMPLATE(class CV, class Out)
(requires Regular<CV>&& Receiver<Out>)void submit(CV, Out out) {
set_value(out, *this);
set_done(out);
template<class CV>
task<std::ptrdiff_t> schedule(CV cv) {
return {cv};
}
};
......@@ -54,24 +65,36 @@ inline inline_constrained_executor_t inline_constrained_executor() {
class inline_time_executor_t {
public:
using properties = property_set<
is_time<>,
is_fifo_sequence<>>;
template<class TP>
struct task {
TP tp_;
using properties = property_set<
is_time<>,
is_executor<>,
is_sender<>,
is_always_blocking<>,
is_fifo_sequence<>,
is_single<>>;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>) //
void submit(Out out) && {
std::this_thread::sleep_until(tp_);
set_value(out, inline_time_executor_t{});
set_done(out);
}
};
auto top() {
return std::chrono::system_clock::now();
}
auto executor() {
return *this;
task<std::chrono::system_clock::time_point> schedule() {
return {top()};
}
PUSHMI_TEMPLATE(class TP, class Out)
(requires Regular<TP>&& Receiver<Out>)void submit(TP tp, Out out) {
std::this_thread::sleep_until(tp);
set_value(out, *this);
set_done(out);
template<class TP>
task<TP> schedule(TP tp) {
return {tp};
}
};
......@@ -86,21 +109,27 @@ inline inline_time_executor_t inline_time_executor() {
}
class inline_executor_t {
public:
using properties = property_set<
is_sender<>,
is_executor<>,
is_always_blocking<>,
is_fifo_sequence<>,
is_single<>>;
public:
using properties = property_set<
is_executor<>,
is_fifo_sequence<>>;
auto executor() {
return *this;
}
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)void submit(Out out) {
set_value(out, *this);
set_done(out);
struct task {
using properties = property_set<
is_sender<>,
is_always_blocking<>,
is_single<>>;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>) //
void submit(Out out) && {
set_value(out, inline_executor_t{});
set_done(out);
}
};
task schedule() {
return {};
}
};
......
......@@ -18,6 +18,7 @@
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/receiver.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <type_traits>
namespace folly {
namespace pushmi {
......@@ -26,7 +27,7 @@ template <class E, class... VN>
class any_many_sender {
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::tuple<VN...>)]; // can hold a V in-situ
std::aligned_union_t<0, std::tuple<VN...>> buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
......@@ -35,12 +36,8 @@ class any_many_sender {
}
struct vtable {
static void s_op(data&, data*) {}
static any_executor<E> s_executor(data&) {
return {};
}
static void s_submit(data&, any_receiver<E, VN...>) {}
void (*op_)(data&, data*) = vtable::s_op;
any_executor<E> (*executor_)(data&) = vtable::s_executor;
void (*submit_)(data&, any_receiver<E, VN...>) = vtable::s_submit;
};
static constexpr vtable const noop_{};
......@@ -53,16 +50,12 @@ class any_many_sender {
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static any_executor<E> executor(data& src) {
return any_executor<E>{
::folly::pushmi::executor(*static_cast<Wrapped*>(src.pobj_))};
}
static void submit(data& src, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>(src.pobj_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
static const vtable vtbl{s::op, s::submit};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
......@@ -71,21 +64,17 @@ class any_many_sender {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
static any_executor<E> executor(data& src) {
return any_executor<E>{::folly::pushmi::executor(
*static_cast<Wrapped*>((void*)src.buffer_))};
new (&dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static void submit(data& src, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>((void*)src.buffer_), std::move(out));
*static_cast<Wrapped*>((void*)&src.buffer_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
new (data_.buffer_) Wrapped(std::move(obj));
static const vtable vtbl{s::op, s::submit};
new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
......@@ -117,11 +106,10 @@ class any_many_sender {
new ((void*)this) any_many_sender(std::move(that));
return *this;
}
any_executor<E> executor() {
return vptr_->executor_(data_);
}
void submit(any_receiver<E, VN...> out) {
vptr_->submit_(data_, std::move(out));
PUSHMI_TEMPLATE(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) //
void submit(Out&& out) {
vptr_->submit_(data_, any_receiver<E, VN...>{(Out &&) out});
}
};
......@@ -130,22 +118,16 @@ template <class E, class... VN>
constexpr typename any_many_sender<E, VN...>::vtable const
any_many_sender<E, VN...>::noop_;
template <class SF, class EXF>
class many_sender<SF, EXF> {
template <class SF>
class many_sender<SF> {
SF sf_;
EXF exf_;
public:
using properties = property_set<is_sender<>, is_many<>>;
constexpr many_sender() = default;
constexpr explicit many_sender(SF sf) : sf_(std::move(sf)) {}
constexpr many_sender(SF sf, EXF exf)
: sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() {
return exf_();
}
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<SF&, Out>)) //
......@@ -154,11 +136,10 @@ class many_sender<SF, EXF> {
}
};
template <PUSHMI_TYPE_CONSTRAINT(Sender<is_many<>>) Data, class DSF, class DEXF>
class many_sender<Data, DSF, DEXF> {
template <PUSHMI_TYPE_CONSTRAINT(Sender<is_many<>>) Data, class DSF>
class many_sender<Data, DSF> {
Data data_;
DSF sf_;
DEXF exf_;
public:
using properties = property_set_insert_t<
......@@ -169,22 +150,23 @@ class many_sender<Data, DSF, DEXF> {
constexpr explicit many_sender(Data data) : data_(std::move(data)) {}
constexpr many_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
constexpr many_sender(Data data, DSF sf, DEXF exf)
: data_(std::move(data)), sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() {
return exf_(data_);
}
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&, Data&, Out>)) //
void submit(Out out) {
void submit(Out out) & {
sf_(data_, std::move(out));
}
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&, Data&&, Out>)) //
void submit(Out out) && {
sf_(std::move(data_), std::move(out));
}
};
template <>
class many_sender<> : public many_sender<ignoreSF, trampolineEXF> {
class many_sender<> : public many_sender<ignoreSF> {
public:
many_sender() = default;
};
......@@ -193,72 +175,47 @@ class many_sender<> : public many_sender<ignoreSF, trampolineEXF> {
// make_many_sender
PUSHMI_INLINE_VAR constexpr struct make_many_sender_fn {
inline auto operator()() const {
return many_sender<ignoreSF, trampolineEXF>{};
return many_sender<ignoreSF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not Sender<SF>)) //
auto
operator()(SF sf) const {
return many_sender<SF, trampolineEXF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<>&& Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(
&&not Sender<SF>)) //
auto
operator()(SF sf, EXF exf) const {
return many_sender<SF, EXF>{std::move(sf), std::move(exf)};
return many_sender<SF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<>&& Sender<Data, is_many<>>) //
auto
operator()(Data d) const {
return many_sender<Data, passDSF, passDEXF>{std::move(d)};
return many_sender<Data, passDSF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_many<>>) //
auto
operator()(Data d, DSF sf) const {
return many_sender<Data, DSF, passDEXF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_many<>>&& Invocable<DEXF&, Data&>) //
auto
operator()(Data d, DSF sf, DEXF exf) const {
return many_sender<Data, DSF, DEXF>{
std::move(d), std::move(sf), std::move(exf)};
return many_sender<Data, DSF>{std::move(d), std::move(sf)};
}
} const make_many_sender{};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
many_sender()->many_sender<ignoreSF, trampolineEXF>;
many_sender()->many_sender<ignoreSF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not Sender<SF>)) //
many_sender(SF)
->many_sender<SF, trampolineEXF>;
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<>&& Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(
&&not Sender<SF>)) //
many_sender(SF, EXF)
->many_sender<SF, EXF>;
->many_sender<SF>;
PUSHMI_TEMPLATE(class Data)
(requires True<>&& Sender<Data, is_many<>>) //
many_sender(Data)
->many_sender<Data, passDSF, passDEXF>;
->many_sender<Data, passDSF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_many<>>) //
many_sender(Data, DSF)
->many_sender<Data, DSF, passDEXF>;
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_many<>>&& Invocable<DEXF&, Data&>) //
many_sender(Data, DSF, DEXF)
->many_sender<Data, DSF, DEXF>;
->many_sender<Data, DSF>;
#endif
template <>
......
......@@ -24,29 +24,35 @@ namespace pushmi {
// very poor perf example executor.
//
struct new_thread_executor {
struct new_thread_executor;
struct new_thread_task {
using properties = property_set<
is_sender<>,
is_executor<>,
is_never_blocking<>,
is_concurrent_sequence<>,
is_single<>>;
new_thread_executor executor() {
return {};
}
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)
void submit(Out out) {
void submit(Out out) && {
std::thread t{[out = std::move(out)]() mutable {
auto tr = ::folly::pushmi::trampoline();
::folly::pushmi::submit(tr, std::move(out));
::folly::pushmi::submit(::folly::pushmi::schedule(tr), std::move(out));
}};
// pass ownership of thread to out
t.detach();
}
};
struct new_thread_executor {
using properties = property_set<is_executor<>, is_concurrent_sequence<>>;
new_thread_task schedule() {
return {};
}
};
inline new_thread_executor new_thread() {
return {};
}
......
......@@ -22,34 +22,23 @@
namespace folly {
namespace pushmi {
namespace detail {
struct single_empty_sender_base : single_sender<ignoreSF, inlineEXF> {
struct single_empty_impl : pipeorigin {
using properties = property_set<
is_sender<>,
is_single<>,
is_always_blocking<>,
is_fifo_sequence<>>;
};
template <class... VN>
struct single_empty_impl {
is_always_blocking<>>;
PUSHMI_TEMPLATE(class Out)
(requires ReceiveValue<Out, VN...>) //
void
operator()(single_empty_sender_base&, Out out) {
(requires Receiver<Out>&& Invocable<decltype(set_done)&, Out&>) //
void submit(Out&& out) {
set_done(out);
}
};
} // namespace detail
namespace operators {
template <class... VN>
auto empty() {
return make_single_sender(
detail::single_empty_sender_base{}, detail::single_empty_impl<VN...>{});
}
inline auto empty() {
return make_single_sender(
detail::single_empty_sender_base{}, detail::single_empty_impl<>{});
inline detail::single_empty_impl empty() {
return {};
}
} // namespace operators
......
......@@ -21,21 +21,20 @@
namespace folly {
namespace pushmi {
namespace detail {
struct single_error_sender_base : single_sender<ignoreSF, inlineEXF> {
struct single_error_sender_base : single_sender<ignoreSF> {
using properties = property_set<
is_sender<>,
is_single<>,
is_always_blocking<>,
is_fifo_sequence<>>;
is_always_blocking<>>;
};
template <class E, class... VN>
template <class E>
struct single_error_impl {
E e_;
PUSHMI_TEMPLATE(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>)
PUSHMI_TEMPLATE(class Base, class Out)
(requires ReceiveError<Out, E>)
void operator()(
single_error_sender_base&,
Out out) {
Base&&,
Out&& out) {
set_error(out, std::move(e_));
}
};
......@@ -43,12 +42,12 @@ struct single_error_impl {
namespace operators {
PUSHMI_TEMPLATE(class... VN, class E)
(requires And<SemiMovable<VN>...>&& SemiMovable<E>)
PUSHMI_TEMPLATE(class E)
(requires MoveConstructible<E>)
auto error(E e) {
return make_single_sender(
detail::single_error_sender_base{},
detail::single_error_impl<E, VN...>{std::move(e)});
detail::single_error_impl<E>{std::move(e)});
}
} // namespace operators
......
......@@ -18,8 +18,9 @@
#include <folly/experimental/pushmi/boosters.h>
#include <folly/experimental/pushmi/concepts.h>
#include <folly/experimental/pushmi/detail/functional.h>
#include <folly/experimental/pushmi/detail/if_constexpr.h>
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/inline.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <folly/experimental/pushmi/flow_many_sender.h>
#include <folly/experimental/pushmi/flow_receiver.h>
#include <folly/experimental/pushmi/flow_single_sender.h>
......@@ -29,7 +30,6 @@
#include <folly/experimental/pushmi/properties.h>
#include <folly/experimental/pushmi/receiver.h>
#include <folly/experimental/pushmi/single_sender.h>
#include <folly/experimental/pushmi/time_single_sender.h>
#include <folly/experimental/pushmi/traits.h>
#include <tuple>
......@@ -46,7 +46,9 @@ PUSHMI_TEMPLATE(class F, class Tuple, std::size_t... Is)
std::declval<F>(),
std::get<Is>(std::declval<Tuple>())...))) //
constexpr decltype(auto)
apply_impl(F&& f, Tuple&& t, std::index_sequence<Is...>) {
apply_impl(F&& f, Tuple&& t, std::index_sequence<Is...>)
noexcept(noexcept(::folly::pushmi::invoke((F &&) f, std::get<Is>((Tuple &&) t)...)))
{
return ::folly::pushmi::invoke((F &&) f, std::get<Is>((Tuple &&) t)...);
}
template <class Tuple_, class Tuple = std::remove_reference_t<Tuple_>>
......@@ -59,53 +61,51 @@ PUSHMI_TEMPLATE(class F, class Tuple)
std::declval<F>(),
std::declval<Tuple>(),
detail::tupidxs<Tuple>{}))) //
constexpr decltype(auto) apply(F&& f, Tuple&& t) {
constexpr decltype(auto) apply(F&& f, Tuple&& t)
noexcept(noexcept(detail::apply_impl((F &&) f, (Tuple &&) t, detail::tupidxs<Tuple>{})))
{
return detail::apply_impl((F &&) f, (Tuple &&) t, detail::tupidxs<Tuple>{});
}
#endif
namespace detail {
template <class Cardinality, bool IsFlow = false>
template <bool IsFlow = false>
struct make_receiver;
template <>
struct make_receiver<is_single<>> : construct_deduced<receiver> {};
struct make_receiver<> : construct_deduced<receiver> {};
template <>
struct make_receiver<is_many<>> : construct_deduced<receiver> {};
template <>
struct make_receiver<is_single<>, true> : construct_deduced<flow_receiver> {};
template <>
struct make_receiver<is_many<>, true> : construct_deduced<flow_receiver> {};
struct make_receiver<true> : construct_deduced<flow_receiver> {};
template <class Cardinality, bool IsFlow>
struct receiver_from_impl {
using MakeReceiver = make_receiver<Cardinality, IsFlow>;
using MakeReceiver = make_receiver<IsFlow>;
template <class... AN>
using receiver_type = ::folly::pushmi::invoke_result_t<MakeReceiver&, AN...>;
PUSHMI_TEMPLATE(class... Ts)
(requires Invocable<MakeReceiver, Ts...>) //
(requires Invocable<MakeReceiver&, Ts...>) //
auto
operator()(std::tuple<Ts...> args) const {
return ::folly::pushmi::apply(MakeReceiver(), std::move(args));
}
PUSHMI_TEMPLATE(
class... Ts,
class... Fns,
class This = std::enable_if_t<sizeof...(Fns) != 0, receiver_from_impl>)
(requires And<SemiMovable<Fns>...>&& Invocable<MakeReceiver, Ts...>&&
class F0,
class... Fns)
(requires And<SemiMovable<F0>, SemiMovable<Fns>...>&& Invocable<MakeReceiver&, Ts...>&&
Invocable<
This,
::folly::pushmi::invoke_result_t<MakeReceiver, Ts...>,
Fns...>) //
const receiver_from_impl&,
receiver_type<Ts...>,
F0, Fns...>) //
auto
operator()(std::tuple<Ts...> args, Fns... fns) const {
return This()(This()(std::move(args)), std::move(fns)...);
operator()(std::tuple<Ts...> args, F0 f0, Fns... fns) const {
return (*this)((*this)(std::move(args)), std::move(f0), std::move(fns)...);
}
PUSHMI_TEMPLATE(class Out, class... Fns)
(requires Receiver<Out>&& And<SemiMovable<Fns>...>) //
(requires not is_v<Out, std::tuple>&& And<MoveConstructible<Fns&&>...>) //
auto
operator()(Out out, Fns... fns) const {
return MakeReceiver()(std::move(out), std::move(fns)...);
operator()(Out&& out, Fns&&... fns) const {
return MakeReceiver()((Out&&)out, (Fns&&)fns...);
}
};
......@@ -118,80 +118,8 @@ template <PUSHMI_TYPE_CONSTRAINT(Sender) In, class... AN>
using receiver_type_t =
typename receiver_from_fn<In>::template receiver_type<AN...>;
template <class In, class FN>
struct submit_transform_out_1 {
FN fn_;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>&& Invocable<FN, Out>&&
SenderTo<In, ::folly::pushmi::invoke_result_t<const FN&, Out>>) //
void
operator()(In& in, Out out) const {
submit(in, fn_(std::move(out)));
}
};
template <class In, class FN>
struct submit_transform_out_2 {
FN fn_;
PUSHMI_TEMPLATE(class CV, class Out)
(requires Receiver<Out>&& Invocable<FN, Out>&& ConstrainedSenderTo<
In,
::folly::pushmi::invoke_result_t<const FN&, Out>>) //
void
operator()(In& in, CV cv, Out out) const {
submit(in, cv, fn_(std::move(out)));
}
};
template <class In, class SDSF>
struct submit_transform_out_3 {
SDSF sdsf_;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>&& Invocable<const SDSF&, In&, Out>) //
void
operator()(In& in, Out out) const {
sdsf_(in, std::move(out));
}
};
template <class In, class TSDSF>
struct submit_transform_out_4 {
TSDSF tsdsf_;
PUSHMI_TEMPLATE(class CV, class Out)
(requires Receiver<Out>&& Invocable<const TSDSF&, In&, CV, Out>) //
void
operator()(In& in, CV cv, Out out) const {
tsdsf_(in, cv, std::move(out));
}
};
PUSHMI_TEMPLATE(class In, class FN)
(requires Sender<In>&& SemiMovable<FN> PUSHMI_BROKEN_SUBSUMPTION(
&&not ConstrainedSender<In>)) //
auto submit_transform_out(FN fn) {
return on_submit(submit_transform_out_1<In, FN>{std::move(fn)});
}
PUSHMI_TEMPLATE(class In, class FN)
(requires ConstrainedSender<In>&& SemiMovable<FN>) //
auto submit_transform_out(FN fn) {
return submit_transform_out_2<In, FN>{std::move(fn)};
}
PUSHMI_TEMPLATE(class In, class SDSF, class TSDSF)
(requires Sender<In>&& SemiMovable<SDSF>&& SemiMovable<TSDSF>
PUSHMI_BROKEN_SUBSUMPTION(&&not ConstrainedSender<In>)) //
auto submit_transform_out(SDSF sdsf, TSDSF) {
return submit_transform_out_3<In, SDSF>{std::move(sdsf)};
}
PUSHMI_TEMPLATE(class In, class SDSF, class TSDSF)
(requires ConstrainedSender<In>&& SemiMovable<SDSF>&& SemiMovable<TSDSF>) //
auto submit_transform_out(SDSF, TSDSF tsdsf) {
return submit_transform_out_4<In, TSDSF>{std::move(tsdsf)};
}
template <
class Cardinality,
bool IsConstrained = false,
bool IsTime = false,
bool IsFlow = false>
struct make_sender;
template <>
......@@ -199,17 +127,11 @@ struct make_sender<is_single<>> : construct_deduced<single_sender> {};
template <>
struct make_sender<is_many<>> : construct_deduced<many_sender> {};
template <>
struct make_sender<is_single<>, false, false, true>
struct make_sender<is_single<>, true>
: construct_deduced<flow_single_sender> {};
template <>
struct make_sender<is_many<>, false, false, true>
struct make_sender<is_many<>, true>
: construct_deduced<flow_many_sender> {};
template <>
struct make_sender<is_single<>, true, true, false>
: construct_deduced<time_single_sender> {};
template <>
struct make_sender<is_single<>, true, false, false>
: construct_deduced<constrained_single_sender> {};
PUSHMI_INLINE_VAR constexpr struct sender_from_fn {
PUSHMI_TEMPLATE(class In, class... FN)
......@@ -218,8 +140,6 @@ PUSHMI_INLINE_VAR constexpr struct sender_from_fn {
operator()(In in, FN&&... fn) const {
using MakeSender = make_sender<
property_set_index_t<properties_t<In>, is_single<>>,
property_query_v<properties_t<In>, is_constrained<>>,
property_query_v<properties_t<In>, is_time<>>,
property_query_v<properties_t<In>, is_flow<>>>;
return MakeSender{}(std::move(in), (FN &&) fn...);
}
......@@ -243,7 +163,7 @@ struct set_value_fn {
public:
template <class... VN>
auto operator()(VN&&... vn) const {
return impl<std::decay_t<VN>...>{(VN &&) vn...};
return impl<std::decay_t<VN>...>{std::tuple<VN&&...>{(VN &&) vn...}};
}
};
......@@ -308,14 +228,31 @@ struct set_starting_fn {
}
};
struct executor_fn {
struct get_executor_fn {
private:
struct impl {
PUSHMI_TEMPLATE(class In)
(requires Sender<In>) //
auto
operator()(In& in) const {
return executor(in);
return get_executor(in);
}
};
public:
auto operator()() const {
return impl{};
}
};
struct make_strand_fn {
private:
struct impl {
PUSHMI_TEMPLATE(class In)
(requires Sender<In>) //
auto
operator()(In& in) const {
return make_strand(in);
}
};
......@@ -333,19 +270,14 @@ struct do_submit_fn {
PUSHMI_TEMPLATE(class In)
(requires SenderTo<In, Out>) //
void
operator()(In& in) {
submit(in, std::move(out_));
operator()(In&& in) && {
submit((In&&)in, std::move(out_));
}
};
template <class TP, class Out>
struct time_impl {
TP tp_;
Out out_;
PUSHMI_TEMPLATE(class In)
(requires TimeSenderTo<In, Out>) //
(requires SenderTo<In, Out>) //
void
operator()(In& in) {
submit(in, std::move(tp_), std::move(out_));
operator()(In&& in) & {
submit((In&&)in, out_);
}
};
......@@ -356,11 +288,22 @@ struct do_submit_fn {
operator()(Out out) const {
return impl<Out>{std::move(out)};
}
PUSHMI_TEMPLATE(class TP, class Out)
(requires Receiver<Out>) //
auto
operator()(TP tp, Out out) const {
return time_impl<TP, Out>{std::move(tp), std::move(out)};
};
struct do_schedule_fn {
private:
struct impl {
PUSHMI_TEMPLATE(class Ex, class... VN)
(requires Executor<Ex>) //
auto
operator()(Ex& ex, VN&&... vn) {
return schedule(ex, (VN&&)vn...);
}
};
public:
auto operator()() const {
return impl{};
}
};
......@@ -368,7 +311,7 @@ struct top_fn {
private:
struct impl {
PUSHMI_TEMPLATE(class In)
(requires ConstrainedSender<In>) //
(requires ConstrainedExecutor<std::decay_t<In>>) //
auto
operator()(In& in) const {
return ::folly::pushmi::top(in);
......@@ -385,9 +328,9 @@ struct now_fn {
private:
struct impl {
PUSHMI_TEMPLATE(class In)
(requires TimeSender<In>) //
(requires TimeExecutor<std::decay_t<In>>) //
auto
operator()(In& in) const {
operator()(In&& in) const {
return ::folly::pushmi::now(in);
}
};
......@@ -406,8 +349,10 @@ PUSHMI_INLINE_VAR constexpr detail::set_done_fn set_done{};
PUSHMI_INLINE_VAR constexpr detail::set_error_fn set_error{};
PUSHMI_INLINE_VAR constexpr detail::set_value_fn set_value{};
PUSHMI_INLINE_VAR constexpr detail::set_starting_fn set_starting{};
PUSHMI_INLINE_VAR constexpr detail::executor_fn executor{};
PUSHMI_INLINE_VAR constexpr detail::get_executor_fn get_executor{};
PUSHMI_INLINE_VAR constexpr detail::make_strand_fn make_strand{};
PUSHMI_INLINE_VAR constexpr detail::do_submit_fn submit{};
PUSHMI_INLINE_VAR constexpr detail::do_schedule_fn schedule{};
PUSHMI_INLINE_VAR constexpr detail::now_fn now{};
PUSHMI_INLINE_VAR constexpr detail::top_fn top{};
......
......@@ -39,13 +39,13 @@ struct filter_fn {
template <class In, class Predicate>
struct submit_impl {
Predicate p_;
PUSHMI_TEMPLATE(class Out)
PUSHMI_TEMPLATE(class SIn, class Out)
(requires Receiver<Out>)
auto operator()(Out out) const {
return ::folly::pushmi::detail::receiver_from_fn<In>()(
auto operator()(SIn&& in, Out out) const {
submit((In&&)in, ::folly::pushmi::detail::receiver_from_fn<In>()(
std::move(out),
// copy 'p' to allow multiple calls to submit
on_value_impl<In, Predicate>{p_});
on_value_impl<In, Predicate>{p_}));
}
};
template <class Predicate>
......@@ -56,8 +56,7 @@ struct filter_fn {
auto operator()(In in) const {
return ::folly::pushmi::detail::sender_from(
std::move(in),
::folly::pushmi::detail::submit_transform_out<In>(
submit_impl<In, Predicate>{p_}));
submit_impl<In, Predicate>{p_});
}
};
......
......@@ -17,6 +17,7 @@
#include <folly/experimental/pushmi/o/extension_operators.h>
#include <folly/experimental/pushmi/o/submit.h>
#include <folly/Function.h>
namespace folly {
namespace pushmi {
......@@ -28,38 +29,46 @@ struct for_each_fn {
struct subset {
using properties = property_set<PN...>;
};
template<class Up>
struct request_fn {
Up up_;
explicit request_fn(Up up) : up_(std::move(up)) {}
request_fn(request_fn&& o) : up_(std::move(o.up_)) {}
void operator()(std::ptrdiff_t requested) {
::folly::pushmi::set_value(up_, requested);
}
};
template <class In, class Out>
struct Pull : Out {
explicit Pull(Out out) : Out(std::move(out)) {}
struct Pull {
Out out_;
explicit Pull(Out out) : out_(std::move(out)) {}
using properties =
property_set_insert_t<properties_t<Out>, property_set<is_flow<>>>;
std::function<void(std::ptrdiff_t)> pull;
folly::Function<void(std::ptrdiff_t)> pull;
template <class... VN>
void value(VN&&... vn) {
::folly::pushmi::set_value(static_cast<Out&>(*this), (VN &&) vn...);
::folly::pushmi::set_value(out_, (VN &&) vn...);
pull(1);
}
template <class E>
void error(E&& e) {
void error(E&& e) noexcept {
// break circular reference
pull = nullptr;
::folly::pushmi::set_error(static_cast<Out&>(*this), (E &&) e);
::folly::pushmi::set_error(out_, (E &&) e);
}
void done() {
// break circular reference
pull = nullptr;
::folly::pushmi::set_done(static_cast<Out&>(*this));
::folly::pushmi::set_done(out_);
}
PUSHMI_TEMPLATE(class Up)
(requires Receiver<Up> && ReceiveValue<Up, std::ptrdiff_t>)
(requires ReceiveValue<Up, std::ptrdiff_t>)
void starting(Up up) {
pull = [up = std::move(up)](std::ptrdiff_t requested) mutable {
::folly::pushmi::set_value(up, requested);
};
pull = request_fn<Up>{std::move(up)};
pull(1);
}
PUSHMI_TEMPLATE(class Up)
(requires ReceiveValue<Up>)
(requires ReceiveValue<Up> && not ReceiveValue<Up, std::ptrdiff_t>)
void starting(Up) {}
};
template <class... AN>
......
......@@ -34,23 +34,22 @@ namespace operators {
PUSHMI_INLINE_VAR constexpr struct from_fn {
private:
struct sender_base : many_sender<ignoreSF, inlineEXF> {
struct sender_base : many_sender<> {
using properties = property_set<
is_sender<>,
is_many<>,
is_always_blocking<>,
is_fifo_sequence<>>;
is_always_blocking<>>;
};
template <class I, class S>
struct out_impl {
I begin_;
S end_;
PUSHMI_TEMPLATE(class Out)
PUSHMI_TEMPLATE(class In, class Out)
(requires ReceiveValue<
Out,
typename std::iterator_traits<I>::value_type>) //
void
operator()(sender_base&, Out out) const {
operator()(In&&, Out out) const {
auto c = begin_;
for (; c != end_; ++c) {
set_value(out, *c);
......@@ -105,7 +104,7 @@ struct flow_from_up {
}
// submit work to exec
::folly::pushmi::submit(
p->exec, make_receiver([p = p, requested](auto) {
::folly::pushmi::schedule(p->exec), make_receiver([p = p, requested](auto) {
auto remaining = requested;
// this loop is structured to work when there is
// re-entrancy out.value in the loop may call up.value.
......@@ -126,13 +125,13 @@ struct flow_from_up {
void error(E) noexcept {
p->stop.store(true);
::folly::pushmi::submit(
p->exec, make_receiver([p = p](auto) { set_done(p->out); }));
::folly::pushmi::schedule(p->exec), make_receiver([p = p](auto) { set_done(p->out); }));
}
void done() {
p->stop.store(true);
::folly::pushmi::submit(
p->exec, make_receiver([p = p](auto) { set_done(p->out); }));
::folly::pushmi::schedule(p->exec), make_receiver([p = p](auto) { set_done(p->out); }));
}
};
......@@ -142,19 +141,19 @@ PUSHMI_INLINE_VAR constexpr struct flow_from_fn {
struct out_impl {
I begin_;
S end_;
mutable Exec exec_;
Exec exec_;
PUSHMI_TEMPLATE(class Out)
(requires ReceiveValue<
Out,
typename std::iterator_traits<I>::value_type>) //
void
operator()(Out out) const {
operator()(Out out) {
using Producer = flow_from_producer<I, S, Out, Exec>;
auto p = std::make_shared<Producer>(
begin_, end_, std::move(out), exec_, false);
::folly::pushmi::submit(
exec_, make_receiver([p](auto) {
::folly::pushmi::schedule(exec_), make_receiver([p](auto) {
// pass reference for cancellation.
set_starting(p->out, make_receiver(flow_from_up<Producer>{p}));
}));
......@@ -181,14 +180,14 @@ PUSHMI_INLINE_VAR constexpr struct flow_from_fn {
PUSHMI_TEMPLATE(class I, class S, class Exec)
(requires DerivedFrom<
typename std::iterator_traits<I>::iterator_category,
std::forward_iterator_tag>&& Sender<Exec, is_single<>, is_executor<>>) //
std::forward_iterator_tag>&& Executor<Exec>) //
auto
operator()(I begin, S end, Exec exec) const {
return make_flow_many_sender(out_impl<I, S, Exec>{begin, end, exec});
}
PUSHMI_TEMPLATE(class R, class Exec)
(requires Range<R>&& Sender<Exec, is_single<>, is_executor<>>) //
(requires Range<R>&& Executor<Exec>) //
auto
operator()(R&& range, Exec exec) const {
return (*this)(std::begin(range), std::end(range), exec);
......
......@@ -26,24 +26,23 @@ namespace operators {
PUSHMI_INLINE_VAR constexpr struct just_fn {
private:
struct sender_base : single_sender<ignoreSF, inlineEXF> {
struct sender_base : single_sender<> {
using properties = property_set<
is_sender<>,
is_single<>,
is_always_blocking<>,
is_fifo_sequence<>>;
is_always_blocking<>>;
};
template <class... VN>
struct impl {
std::tuple<VN...> vn_;
PUSHMI_TEMPLATE(class Out)
PUSHMI_TEMPLATE(class In, class Out)
(requires ReceiveValue<Out, VN...>) //
void
operator()(sender_base&, Out out) {
operator()(In&&, Out&& out) {
::folly::pushmi::apply(
::folly::pushmi::set_value,
std::tuple_cat(std::tuple<Out&>{out}, std::move(vn_)));
set_done(std::move(out));
set_done(out);
}
};
......
......@@ -34,66 +34,38 @@ struct on_fn {
submit(in_, std::move(out_));
}
};
template <class In, class ExecutorFactory>
struct out_impl {
ExecutorFactory ef_;
PUSHMI_TEMPLATE(class Out)
template <class Factory>
struct submit_impl {
Factory ef_;
PUSHMI_TEMPLATE(class In, class Out)
(requires SenderTo<In, Out>) //
void
operator()(In& in, Out out) const {
auto exec = ef_();
operator()(In&& in, Out out) const {
auto exec = ::folly::pushmi::make_strand(ef_);
submit(
exec,
::folly::pushmi::make_receiver(
on_value_impl<In, Out>{in, std::move(out)}));
::folly::pushmi::schedule(exec),
::folly::pushmi::make_receiver(on_value_impl<std::decay_t<In>, Out>{
(In&&) in, std::move(out)}));
}
};
template <class In, class TP, class Out>
struct time_on_value_impl {
In in_;
TP at_;
Out out_;
void operator()(any) {
submit(in_, at_, std::move(out_));
}
};
template <class In, class ExecutorFactory>
struct time_out_impl {
ExecutorFactory ef_;
PUSHMI_TEMPLATE(class TP, class Out)
(requires TimeSenderTo<In, Out>) //
void
operator()(In& in, TP at, Out out) const {
auto exec = ef_();
submit(
exec,
at,
::folly::pushmi::make_receiver(
time_on_value_impl<In, TP, Out>{in, at, std::move(out)}));
}
};
template <class ExecutorFactory>
struct in_impl {
ExecutorFactory ef_;
template <class Factory>
struct adapt_impl {
Factory ef_;
PUSHMI_TEMPLATE(class In)
(requires Sender<In>) //
(requires Sender<std::decay_t<In>>) //
auto
operator()(In in) const {
operator()(In&& in) const {
return ::folly::pushmi::detail::sender_from(
std::move(in),
detail::submit_transform_out<In>(
out_impl<In, ExecutorFactory>{ef_},
time_out_impl<In, ExecutorFactory>{ef_}));
(In&&) in, submit_impl<Factory>{ef_});
}
};
public:
PUSHMI_TEMPLATE(class ExecutorFactory)
(requires Invocable<ExecutorFactory&>&&
Executor<invoke_result_t<ExecutorFactory&>>) //
PUSHMI_TEMPLATE(class Factory)
(requires StrandFactory<Factory>) //
auto
operator()(ExecutorFactory ef) const {
return in_impl<ExecutorFactory>{std::move(ef)};
operator()(Factory ef) const {
return adapt_impl<Factory>{std::move(ef)};
}
};
......
/*
* Copyright 2018-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/experimental/pushmi/boosters.h>
#include <folly/experimental/pushmi/detail/opt.h>
#include <folly/experimental/pushmi/o/extension_operators.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <functional>
namespace folly {
namespace pushmi {
namespace detail {
struct schedule_fn {
private:
// TODO - only move, move-only types..
// if out can be copied, then schedule can be called multiple
// times..
struct fn {
PUSHMI_TEMPLATE(class Exec)
(requires Executor<Exec>) //
auto
operator()(Exec& ex) {
return schedule(ex);
}
};
public:
auto operator()() const {
return schedule_fn::fn{};
}
};
struct schedule_at_fn {
private:
// TODO - only move, move-only types..
// if out can be copied, then schedule can be called multiple
// times..
template <class CV>
struct fn {
CV at_;
PUSHMI_TEMPLATE(class Exec)
(requires ConstrainedExecutor<Exec>) //
auto
operator()(Exec& ex) {
return schedule(ex, std::move(at_));
}
};
public:
template <class CV>
auto operator()(CV&& at) const {
return schedule_at_fn::fn<CV>{(CV &&) at};
}
};
struct schedule_after_fn {
private:
// TODO - only move, move-only types..
// if out can be copied, then schedule can be called multiple
// times..
template <class Dur>
struct fn {
Dur after_;
PUSHMI_TEMPLATE(class Exec)
(requires TimeExecutor<Exec>) //
auto
operator()(Exec& ex) {
return schedule(ex, now(ex) + after_);
}
};
public:
template <class Dur>
auto operator()(Dur&& after) const {
return schedule_after_fn::fn<Dur>{(Dur &&) after};
}
};
} // namespace detail
namespace operators {
PUSHMI_INLINE_VAR constexpr detail::schedule_fn schedule{};
PUSHMI_INLINE_VAR constexpr detail::schedule_at_fn schedule_at{};
PUSHMI_INLINE_VAR constexpr detail::schedule_after_fn schedule_after{};
} // namespace operators
} // namespace pushmi
} // namespace folly
......@@ -16,10 +16,9 @@
#pragma once
#include <folly/experimental/pushmi/boosters.h>
#include <folly/experimental/pushmi/detail/if_constexpr.h>
#include <folly/experimental/pushmi/detail/opt.h>
#include <folly/experimental/pushmi/o/extension_operators.h>
#include <folly/experimental/pushmi/time_single_sender.h>
#include <folly/experimental/pushmi/o/schedule.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <functional>
......@@ -31,15 +30,7 @@ namespace submit_detail {
PUSHMI_CONCEPT_DEF(
template(class In, class... AN) //
(concept AutoSenderTo)(In, AN...), //
Sender<In>&& SenderTo<In, receiver_type_t<In, AN...>>);
PUSHMI_CONCEPT_DEF(
template(class In, class... AN) //
(concept AutoConstrainedSenderTo)(In, AN...), //
ConstrainedSenderTo<In, receiver_type_t<In, AN...>>);
PUSHMI_CONCEPT_DEF(
template(class In, class... AN) //
(concept AutoTimeSenderTo)(In, AN...), //
TimeSenderTo<In, receiver_type_t<In, AN...>>);
SenderTo<In, receiver_type_t<std::decay_t<In>, AN...>>);
} // namespace submit_detail
struct submit_fn {
......@@ -48,96 +39,64 @@ struct submit_fn {
// if out can be copied, then submit can be called multiple
// times..
template <class... AN>
struct fn {
struct fn : pipeorigin {
explicit fn(AN&&... an) : args_((AN &&) an...) {}
std::tuple<AN...> args_;
PUSHMI_TEMPLATE(class In)
(requires submit_detail::AutoSenderTo<In, AN...>) //
In
operator()(In in) {
auto out{
::folly::pushmi::detail::receiver_from_fn<In>{}(std::move(args_))};
::folly::pushmi::submit(in, std::move(out));
return in;
(requires //
submit_detail::AutoSenderTo<In&&, AN...>) //
void
operator()(In&& in) {
using maker_t = ::folly::pushmi::detail::receiver_from_fn<std::decay_t<In>>;
using receiver_t = invoke_result_t<maker_t, std::tuple<AN...>&&>;
receiver_t out{
maker_t{}(std::move(args_))};
::folly::pushmi::submit((In &&) in, std::move(out));
}
};
public:
template <class... AN>
auto operator()(AN&&... an) const {
return submit_fn::fn<AN...>{std::tuple<AN...>{(AN &&) an...}};
return submit_fn::fn<AN...>{(AN &&) an...};
}
};
struct submit_at_fn {
struct blocking_submit_fn {
private:
template <class TP, class... AN>
struct fn {
TP at_;
std::tuple<AN...> args_;
PUSHMI_TEMPLATE(class In)
(requires submit_detail::AutoTimeSenderTo<In, AN...>) //
In
operator()(In in) {
auto out{
::folly::pushmi::detail::receiver_from_fn<In>()(std::move(args_))};
::folly::pushmi::submit(in, std::move(at_), std::move(out));
return in;
}
struct lock_state {
std::atomic<bool> notified{false}; // ensures that state survives until
// the stack is unwound
std::atomic<bool> done{false}; // ensures that blocking completes after
// the source called done/error
std::atomic<int> nested{0}; // tracks the pending nested executors
// and executions
std::mutex lock;
std::condition_variable signaled;
};
public:
PUSHMI_TEMPLATE(class TP, class... AN)
(requires Regular<TP>) //
auto
operator()(TP at, AN... an) const {
return submit_at_fn::fn<TP, AN...>{std::move(at),
std::tuple<AN...>{(AN &&) an...}};
}
};
struct submit_after_fn {
private:
template <class D, class... AN>
struct fn {
D after_;
std::tuple<AN...> args_;
PUSHMI_TEMPLATE(class In)
(requires submit_detail::AutoTimeSenderTo<In, AN...>) //
In
operator()(In in) {
// TODO - only move, move-only types..
// if out can be copied, then submit can be called multiple
// times..
auto out{
::folly::pushmi::detail::receiver_from_fn<In>()(std::move(args_))};
auto at = ::folly::pushmi::now(in) + std::move(after_);
::folly::pushmi::submit(in, std::move(at), std::move(out));
return in;
struct protect_stack {
lock_state* state_;
~protect_stack() {
if (--state_->nested == 0) {
std::unique_lock<std::mutex> guard{state_->lock};
state_->signaled.notify_all();
state_->notified.exchange(true);
}
}
protect_stack(lock_state* state) : state_(state) {
++state_->nested;
}
};
public:
PUSHMI_TEMPLATE(class D, class... AN)
(requires Regular<D>) //
auto
operator()(D after, AN... an) const {
return submit_after_fn::fn<D, AN...>{std::move(after),
std::tuple<AN...>{(AN &&) an...}};
}
};
template<class Task>
struct nested_task_impl;
struct blocking_submit_fn {
private:
struct lock_state {
bool done{false};
std::atomic<int> nested{0};
std::mutex lock;
std::condition_variable signaled;
};
template <class Out>
struct nested_receiver_impl;
PUSHMI_TEMPLATE(class Exec)
(requires Sender<Exec>&& Executor<Exec>) //
(requires Executor<Exec>) //
struct nested_executor_impl {
nested_executor_impl(lock_state* state, Exec ex)
: state_(state), ex_(std::move(ex)) {}
......@@ -148,12 +107,12 @@ struct blocking_submit_fn {
using test_for_this = nested_executor_impl<U>;
PUSHMI_TEMPLATE(class Ex)
(requires Sender<Ex>&& Executor<Ex>&& detail::is_v<Ex, test_for_this>) //
(requires Executor<Ex>&& detail::is_v<Ex, test_for_this>) //
static auto make(lock_state*, Ex ex) {
return ex;
}
PUSHMI_TEMPLATE(class Ex)
(requires Sender<Ex>&& Executor<Ex> &&
(requires Executor<Ex> &&
not detail::is_v<Ex, test_for_this>) //
static auto make(lock_state* state, Ex ex) {
return nested_executor_impl<Ex>{state, ex};
......@@ -161,32 +120,38 @@ struct blocking_submit_fn {
using properties = properties_t<Exec>;
auto executor() {
return make(state_, ::folly::pushmi::executor(ex_));
}
PUSHMI_TEMPLATE(class... ZN)
(requires Constrained<Exec>) //
auto top() {
return ::folly::pushmi::top(ex_);
}
PUSHMI_TEMPLATE(class CV, class Out)
(requires Receiver<Out>&& Constrained<Exec>) //
void submit(CV cv, Out out) {
++state_->nested;
::folly::pushmi::submit(
ex_, cv, nested_receiver_impl<Out>{state_, std::move(out)});
template<class... VN>
auto schedule(VN&&... vn) {
auto protected_scope = protect_stack{state_};
return nested_task_impl<decltype(::folly::pushmi::schedule(ex_, (VN&&)vn...))>{
state_, ::folly::pushmi::schedule(ex_, (VN&&)vn...)};
}
};
template<class Task>
struct nested_task_impl {
nested_task_impl(lock_state* state, Task t)
: state_(state), t_(std::move(t)) {}
lock_state* state_;
Task t_;
using properties = properties_t<Task>;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out> && not Constrained<Exec>) //
void submit(Out out) {
++state_->nested;
(requires Receiver<Out>) //
void submit(Out out) && {
auto protected_scope = protect_stack{state_};
++state_->nested; // reversed in nested_receiver_impl ::done/::error
::folly::pushmi::submit(
ex_, nested_receiver_impl<Out>{state_, std::move(out)});
std::move(t_), nested_receiver_impl<Out>{state_, std::move(out)});
}
};
template <class Out>
struct nested_receiver_impl {
nested_receiver_impl(lock_state* state, Out out)
......@@ -198,31 +163,21 @@ struct blocking_submit_fn {
template <class V>
void value(V&& v) {
std::exception_ptr e;
auto protected_scope = protect_stack{state_};
using executor_t = remove_cvref_t<V>;
auto n = nested_executor_impl<executor_t>::make(state_, (V &&) v);
set_value(out_, any_executor_ref<>{n});
set_value(out_, n);
}
template <class E>
void error(E&& e) noexcept {
auto protected_scope = protect_stack{state_};
set_error(out_, (E &&) e);
if (--state_->nested == 0) {
state_->signaled.notify_all();
}
--state_->nested; // reverses nested_task_impl::submit
}
void done() {
std::exception_ptr e;
try {
set_done(out_);
} catch (...) {
e = std::current_exception();
}
if (--state_->nested == 0) {
state_->signaled.notify_all();
}
if (e) {
std::rethrow_exception(e);
}
auto protected_scope = protect_stack{state_};
set_done(out_);
--state_->nested; // reverses nested_task_impl::submit
}
};
struct nested_executor_impl_fn {
......@@ -244,12 +199,8 @@ struct blocking_submit_fn {
std::decay_t<Value>>>) //
void
operator()(Out out, Value&& v) const {
++state_->nested;
auto protected_scope = protect_stack{state_};
set_value(out, nested_executor_impl_fn{}(state_, (Value &&) v));
if (--state_->nested == 0) {
std::unique_lock<std::mutex> guard{state_->lock};
state_->signaled.notify_all();
}
}
PUSHMI_TEMPLATE(class Out, class... VN)
(requires True<>&& ReceiveValue<Out, VN...> &&
......@@ -265,10 +216,10 @@ struct blocking_submit_fn {
(requires ReceiveError<Out, E>) //
void
operator()(Out out, E e) const noexcept {
auto protected_scope = protect_stack{state_};
set_error(out, std::move(e));
std::unique_lock<std::mutex> guard{state_->lock};
state_->done = true;
state_->signaled.notify_all();
state_->done.exchange(true);
--state_->nested; // reverses blocking_submit_fn::fn
}
};
struct on_done_impl {
......@@ -277,10 +228,10 @@ struct blocking_submit_fn {
(requires Receiver<Out>) //
void
operator()(Out out) const {
auto protected_scope = protect_stack{state_};
set_done(out);
std::unique_lock<std::mutex> guard{state_->lock};
state_->done = true;
state_->signaled.notify_all();
state_->done.exchange(true);
--state_->nested; // reverses blocking_submit_fn::fn
}
};
......@@ -302,8 +253,8 @@ struct blocking_submit_fn {
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>&& SenderTo<In, Out>) //
void
operator()(In& in, Out out) const {
::folly::pushmi::submit(in, std::move(out));
operator()(In&& in, Out out) const {
::folly::pushmi::submit((In &&) in, std::move(out));
}
};
// TODO - only move, move-only types..
......@@ -314,26 +265,27 @@ struct blocking_submit_fn {
std::tuple<AN...> args_;
PUSHMI_TEMPLATE(class In)
(requires Sender<In>&& Invocable<
(requires Sender<std::decay_t<In>>&& Invocable<
submit_impl<In>&,
In&,
In&&,
::folly::pushmi::invoke_result_t<
receiver_impl<In>,
receiver_impl<std::decay_t<In>>&,
lock_state*,
std::tuple<AN...>&&>> &&
not AlwaysBlocking<In>) //
In
operator()(In in) {
void
operator()(In&& in) {
lock_state state{};
auto make = receiver_impl<In>{};
auto make = receiver_impl<std::decay_t<In>>{};
auto submit = submit_impl<In>{};
submit(in, make(&state, std::move(args_)));
++state.nested; // reversed by on_done_impl and on_error_impl
submit((In &&) in, make(&state, std::move(args_)));
std::unique_lock<std::mutex> guard{state.lock};
state.signaled.wait(
guard, [&] { return state.done && state.nested.load() == 0; });
return in;
guard, [&] { return state.done.load() && state.nested.load() == 0; });
while (!state.notified.load()) {}
}
};
......@@ -392,8 +344,6 @@ struct get_fn {
namespace operators {
PUSHMI_INLINE_VAR constexpr detail::submit_fn submit{};
PUSHMI_INLINE_VAR constexpr detail::submit_at_fn submit_at{};
PUSHMI_INLINE_VAR constexpr detail::submit_after_fn submit_after{};
PUSHMI_INLINE_VAR constexpr detail::blocking_submit_fn blocking_submit{};
template <class T>
PUSHMI_INLINE_VAR constexpr detail::get_fn<T> get{};
......
......@@ -29,26 +29,26 @@ struct switch_on_error_fn {
struct on_error_impl {
ErrorSelector es_;
PUSHMI_TEMPLATE(class Out, class E)
(requires Receiver<Out>&& Invocable<const ErrorSelector&, E>&&
(requires Receiver<Out>&& Invocable<ErrorSelector&, E>&&
SenderTo<::folly::pushmi::invoke_result_t<ErrorSelector&, E>, Out>)
void operator()(Out& out, E&& e) const noexcept {
void operator()(Out& out, E&& e) noexcept {
static_assert(
::folly::pushmi::NothrowInvocable<const ErrorSelector&, E>,
::folly::pushmi::NothrowInvocable<ErrorSelector&, E>,
"switch_on_error - error selector function must be noexcept");
auto next = es_((E &&) e);
submit(next, out);
submit(std::move(next), std::move(out));
}
};
template <class In, class ErrorSelector>
struct out_impl {
ErrorSelector es_;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)
auto operator()(Out out) const {
return ::folly::pushmi::detail::receiver_from_fn<In>()(
std::move(out),
PUSHMI_TEMPLATE(class SIn, class Out)
(requires Receiver<std::decay_t<Out>>)
auto operator()(SIn&& in, Out&& out) const {
submit((In&&)in, ::folly::pushmi::detail::receiver_from_fn<In>()(
(Out&&)out,
// copy 'es' to allow multiple calls to submit
::folly::pushmi::on_error(on_error_impl<ErrorSelector>{es_}));
::folly::pushmi::on_error(on_error_impl<ErrorSelector>{es_})));
}
};
template <class ErrorSelector>
......@@ -56,11 +56,10 @@ struct switch_on_error_fn {
ErrorSelector es_;
PUSHMI_TEMPLATE(class In)
(requires Sender<In>)
auto operator()(In in) const {
auto operator()(In&& in) {
return ::folly::pushmi::detail::sender_from(
std::move(in),
::folly::pushmi::detail::submit_transform_out<In>(
out_impl<In, ErrorSelector>{es_}));
(In&&)in,
out_impl<In, ErrorSelector>{std::move(es_)});
}
};
......
......@@ -24,7 +24,7 @@ namespace pushmi {
namespace detail {
PUSHMI_TEMPLATE(class SideEffects, class Out)
(requires Receiver<SideEffects>&& Receiver<Out>)
(requires Receiver<SideEffects>&& Receiver<Out>) //
struct tap_ {
SideEffects sideEffects;
Out out;
......@@ -33,27 +33,25 @@ struct tap_ {
using properties = properties_t<Out>;
PUSHMI_TEMPLATE(class... VN)
(requires ReceiveValue<SideEffects, const std::remove_reference_t<VN>...>&&
ReceiveValue<
Out,
std::remove_reference_t<VN>...>)
void value(VN&&... vn) {
(requires ReceiveValue<SideEffects&, const std::remove_reference_t<VN>&...>&&
ReceiveValue<Out&, VN...>) //
void value(VN&&... vn) {
set_value(sideEffects, as_const(vn)...);
set_value(out, (VN &&) vn...);
}
PUSHMI_TEMPLATE(class E)
(requires ReceiveError<SideEffects, const E>&&
ReceiveError<Out, E>)
void error(E e) noexcept {
(requires ReceiveError<SideEffects&, const std::remove_reference_t<E>&>&&
ReceiveError<Out&, E>) //
void error(E&& e) noexcept {
set_error(sideEffects, as_const(e));
set_error(out, std::move(e));
set_error(out, (E&&) e);
}
void done() {
set_done(sideEffects);
set_done(out);
}
PUSHMI_TEMPLATE(class Up, class UUp = std::remove_reference_t<Up>)
(requires FlowReceiver<SideEffects>&& FlowReceiver<Out>)
PUSHMI_TEMPLATE(class Up)
(requires FlowReceiver<SideEffects>&& FlowReceiver<Out>) //
void starting(
Up&& up) {
// up is not made const because sideEffects is allowed to call methods on up
......@@ -64,64 +62,65 @@ struct tap_ {
PUSHMI_INLINE_VAR constexpr struct make_tap_fn {
PUSHMI_TEMPLATE(class SideEffects, class Out)
(requires Receiver<SideEffects>&& Receiver<Out>&&
Receiver<tap_<SideEffects, Out>>)
auto operator()(SideEffects se, Out out) const {
return tap_<SideEffects, Out>{std::move(se), std::move(out)};
(requires Receiver<std::decay_t<SideEffects>>&& Receiver<std::decay_t<Out>>&&
Receiver<tap_<std::decay_t<SideEffects>, std::decay_t<Out>>>) //
auto operator()(const SideEffects& se, Out&& out) const {
return tap_<std::decay_t<SideEffects>, std::decay_t<Out>>{se, (Out &&) out};
}
} const make_tap{};
struct tap_fn {
private:
PUSHMI_TEMPLATE(class In, class SideEffects)
(requires Sender<In>&& Receiver<SideEffects>)
(requires SenderTo<In, SideEffects>) //
static auto impl(
In in,
SideEffects sideEffects) {
In&& in,
SideEffects&& sideEffects) {
return ::folly::pushmi::detail::sender_from(
std::move(in),
::folly::pushmi::detail::submit_transform_out<In>(
out_impl<In, SideEffects>{std::move(sideEffects)}));
(In &&) in,
submit_impl<In, std::decay_t<SideEffects>>{(SideEffects &&)
sideEffects});
}
template <class... AN>
struct in_impl {
struct adapt_impl {
std::tuple<AN...> args_;
PUSHMI_TEMPLATE(class In)
(requires Sender<In>)
auto operator()(In in) {
(requires Sender<std::decay_t<In>>) //
auto operator()(In&& in) {
return tap_fn::impl(
std::move(in),
(In &&) in,
::folly::pushmi::detail::receiver_from_fn<In>()(std::move(args_)));
}
};
PUSHMI_TEMPLATE(class In, class SideEffects)
(requires Sender<In>&& Receiver<SideEffects>)
struct out_impl {
(requires Sender<std::decay_t<In>>&&
Receiver<SideEffects>) //
struct submit_impl {
SideEffects sideEffects_;
template<class Out>
using tap_t =
decltype(
detail::make_tap(
std::declval<const SideEffects&>(),
std::declval<Out>()));
template<class Out>
using receiver_t = invoke_result_t<receiver_from_fn<In>, tap_t<Out>>;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>&& SenderTo<In, Out>&& SenderTo<
In,
receiver_t<Out>>)
auto operator()(Out out) const {
auto gang{::folly::pushmi::detail::receiver_from_fn<In>()(
detail::make_tap(sideEffects_, std::move(out)))};
return gang;
template <class Out>
using tap_t = decltype(detail::make_tap(
std::declval<const SideEffects&>(),
std::declval<Out>()));
template <class Out>
using receiver_t =
invoke_result_t<receiver_from_fn<std::decay_t<In>>, tap_t<Out>>;
PUSHMI_TEMPLATE(class Data, class Out)
(requires Receiver<std::decay_t<Out>>
&& SenderTo<In, Out>&&
SenderTo<In, receiver_t<Out>>) //
auto operator()(Data&& in, Out&& out) const {
auto gang{::folly::pushmi::detail::receiver_from_fn<std::decay_t<In>>()(
detail::make_tap(sideEffects_, (Out &&) out))};
submit((In &&) in, std::move(gang));
}
};
public:
template <class... AN>
auto operator()(AN... an) const {
return in_impl<AN...>{std::tuple<AN...>{std::move(an)...}};
return adapt_impl<AN...>{std::tuple<AN...>{std::move(an)...}};
}
};
......
......@@ -110,7 +110,7 @@ struct transform_on<F, is_many<>, true> {
return make_flow_receiver(std::move(out), on_value(*this));
}
template <class Out, class V0, class... VN>
auto operator()(Out& out, V0&& v0, VN&&... vn) {
void operator()(Out& out, V0&& v0, VN&&... vn) {
using Result = ::folly::pushmi::invoke_result_t<F, V0, VN...>;
static_assert(
::folly::pushmi::SemiMovable<Result>,
......@@ -125,21 +125,34 @@ struct transform_on<F, is_many<>, true> {
struct transform_fn {
private:
template <class F, class In>
struct submit_impl {
F f_;
PUSHMI_TEMPLATE(class SIn, class Out)
(requires Receiver<std::decay_t<Out>>) //
auto
operator()(SIn&& in, Out&& out) const {
using Cardinality = property_set_index_t<properties_t<In>, is_single<>>;
// copy 'f_' to allow multiple calls to connect to multiple 'in'
::folly::pushmi::submit(
(In &&) in,
transform_on<
F,
Cardinality,
property_query_v<properties_t<In>, is_flow<>>>{f_}((Out &&) out));
}
};
template <class F>
struct impl {
struct adapt_impl {
F f_;
PUSHMI_TEMPLATE(class In)
(requires Sender<In>)
auto operator()(In in) const {
using Cardinality = property_set_index_t<properties_t<In>, is_single<>>;
(requires Sender<std::decay_t<In>>) //
auto
operator()(In&& in) const {
// copy 'f_' to allow multiple calls to connect to multiple 'in'
return ::folly::pushmi::detail::sender_from(
std::move(in),
::folly::pushmi::detail::submit_transform_out<In>(
// copy 'f_' to allow multiple calls to connect to multiple 'in'
transform_on<
F,
Cardinality,
property_query_v<properties_t<In>, is_flow<>>>{f_}));
(In &&) in, submit_impl<F, In&&>{f_});
}
};
......@@ -148,7 +161,7 @@ struct transform_fn {
auto operator()(FN... fn) const {
auto f = ::folly::pushmi::overload(std::move(fn)...);
using F = decltype(f);
return impl<F>{std::move(f)};
return adapt_impl<F>{std::move(f)};
}
};
......
......@@ -24,27 +24,48 @@ namespace pushmi {
namespace detail {
template <class Executor>
template <class Exec>
struct via_fn_base {
Executor exec;
bool done;
explicit via_fn_base(Executor ex) : exec(std::move(ex)), done(false) {}
Exec exec_;
bool done_;
explicit via_fn_base(Exec exec) : exec_(std::move(exec)), done_(false) {}
via_fn_base& via_fn_base_ref() {
return *this;
}
};
template <class Executor, class Out>
struct via_fn_data : public Out, public via_fn_base<Executor> {
via_fn_data(Out out, Executor ex)
: Out(std::move(out)), via_fn_base<Executor>(std::move(ex)) {}
template <class Exec, class Out>
struct via_fn_data : flow_receiver<>, via_fn_base<Exec> {
via_fn_data(Out out, Exec exec)
: via_fn_base<Exec>(std::move(exec)), out_(std::make_shared<Out>(std::move(out))) {}
using Out::done;
using Out::error;
using typename Out::properties;
using properties = properties_t<Out>;
using flow_receiver<>::value;
using flow_receiver<>::error;
using flow_receiver<>::done;
template <class Up>
struct impl {
Up up_;
std::shared_ptr<Out> out_;
void operator()(any) {
set_starting(out_, std::move(up_));
}
};
template<class Up>
void starting(Up&& up) {
if (this->via_fn_base_ref().done_) {
return;
}
submit(
::folly::pushmi::schedule(this->via_fn_base_ref().exec_),
::folly::pushmi::make_receiver(impl<std::decay_t<Up>>{
(Up &&) up, out_}));
}
std::shared_ptr<Out> out_;
};
template <class Out, class Executor>
auto make_via_fn_data(Out out, Executor ex) -> via_fn_data<Executor, Out> {
template <class Out, class Exec>
auto make_via_fn_data(Out out, Exec ex) -> via_fn_data<Exec, Out> {
return {std::move(out), std::move(ex)};
}
......@@ -55,20 +76,20 @@ struct via_fn {
template <class V>
struct impl {
V v_;
Out out_;
std::shared_ptr<Out> out_;
void operator()(any) {
set_value(out_, std::move(v_));
}
};
template <class Data, class V>
void operator()(Data& data, V&& v) const {
if (data.via_fn_base_ref().done) {
if (data.via_fn_base_ref().done_) {
return;
}
submit(
data.via_fn_base_ref().exec,
::folly::pushmi::schedule(data.via_fn_base_ref().exec_),
::folly::pushmi::make_receiver(impl<std::decay_t<V>>{
(V &&) v, std::move(static_cast<Out&>(data))}));
(V &&) v, data.out_}));
}
};
template <class Out>
......@@ -76,87 +97,78 @@ struct via_fn {
template <class E>
struct impl {
E e_;
Out out_;
std::shared_ptr<Out> out_;
void operator()(any) noexcept {
set_error(out_, std::move(e_));
}
};
template <class Data, class E>
void operator()(Data& data, E e) const noexcept {
if (data.via_fn_base_ref().done) {
if (data.via_fn_base_ref().done_) {
return;
}
data.via_fn_base_ref().done = true;
data.via_fn_base_ref().done_ = true;
submit(
data.via_fn_base_ref().exec,
::folly::pushmi::schedule(data.via_fn_base_ref().exec_),
::folly::pushmi::make_receiver(
impl<E>{std::move(e), std::move(static_cast<Out&>(data))}));
impl<E>{std::move(e), std::move(data.out_)}));
}
};
template <class Out>
struct on_done_impl {
struct impl {
Out out_;
std::shared_ptr<Out> out_;
void operator()(any) {
set_done(out_);
}
};
template <class Data>
void operator()(Data& data) const {
if (data.via_fn_base_ref().done) {
if (data.via_fn_base_ref().done_) {
return;
}
data.via_fn_base_ref().done = true;
data.via_fn_base_ref().done_ = true;
submit(
data.via_fn_base_ref().exec,
::folly::pushmi::schedule(data.via_fn_base_ref().exec_),
::folly::pushmi::make_receiver(
impl{std::move(static_cast<Out&>(data))}));
}
};
template <class In, class ExecutorFactory>
struct executor_impl {
ExecutorFactory ef_;
template <class Data>
auto operator()(Data&) const {
return ef_();
impl{std::move(data.out_)}));
}
};
template <class In, class ExecutorFactory>
struct out_impl {
ExecutorFactory ef_;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)
auto operator()(Out out) const {
auto exec = ef_();
return ::folly::pushmi::detail::receiver_from_fn<In>()(
make_via_fn_data(std::move(out), std::move(exec)),
on_value_impl<Out>{},
on_error_impl<Out>{},
on_done_impl<Out>{});
template <class In, class Factory>
struct submit_impl {
Factory ef_;
PUSHMI_TEMPLATE(class SIn, class Out)
(requires Receiver<Out>) //
void
operator()(SIn&& in, Out out) const {
auto exec = ::folly::pushmi::make_strand(ef_);
::folly::pushmi::submit(
(In &&) in,
::folly::pushmi::detail::receiver_from_fn<std::decay_t<In>>()(
make_via_fn_data(std::move(out), std::move(exec)),
on_value_impl<Out>{},
on_error_impl<Out>{},
on_done_impl<Out>{}));
}
};
template <class ExecutorFactory>
struct in_impl {
ExecutorFactory ef_;
template <class Factory>
struct adapt_impl {
Factory ef_;
PUSHMI_TEMPLATE(class In)
(requires Sender<In>)
auto operator()(In in) const {
(requires Sender<In>) //
auto
operator()(In&& in) const {
return ::folly::pushmi::detail::sender_from(
std::move(in),
::folly::pushmi::detail::submit_transform_out<In>(
out_impl<In, ExecutorFactory>{ef_}),
::folly::pushmi::on_executor(
executor_impl<In, ExecutorFactory>{ef_}));
(In&&)in,
submit_impl<In&&, Factory>{ef_});
}
};
public:
PUSHMI_TEMPLATE(class ExecutorFactory)
(requires Invocable<ExecutorFactory&>&&
Executor<invoke_result_t<ExecutorFactory&>>&&
FifoSequence<invoke_result_t<ExecutorFactory&>>)
auto operator()(ExecutorFactory ef) const {
return in_impl<ExecutorFactory>{std::move(ef)};
PUSHMI_TEMPLATE(class Factory)
(requires StrandFactory<Factory>)
auto operator()(Factory ef) const {
return adapt_impl<Factory>{std::move(ef)};
}
};
......
......@@ -15,35 +15,52 @@
*/
#pragma once
#include <folly/experimental/pushmi/traits.h>
#include <folly/experimental/pushmi/concepts.h>
#include <folly/experimental/pushmi/traits.h>
namespace folly {
namespace pushmi {
struct pipeorigin {};
PUSHMI_TEMPLATE(class In, class Op)
(requires PUSHMI_EXP(lazy::Sender<std::decay_t<In>> PUSHMI_AND
(requires PUSHMI_EXP(lazy::Sender<In> PUSHMI_AND
lazy::Invocable<Op&, In>)) //
decltype(auto)
operator|(In&& in, Op op) {
operator|(In&& in, Op&& op) {
return op((In &&) in);
}
PUSHMI_TEMPLATE(class Ex, class Op)
(requires PUSHMI_EXP(lazy::Executor<Ex> PUSHMI_AND
lazy::Invocable<Op&, Ex>)) //
decltype(auto)
operator|(Ex&& ex, Op&& op) {
return op((Ex &&) ex);
}
PUSHMI_TEMPLATE(class Out, class Op)
(requires PUSHMI_EXP(lazy::Receiver<Out> PUSHMI_AND
lazy::Invocable<Op&, Out>)) //
decltype(auto)
operator|(Out&& out, Op&& op) {
return op((Out &&) out);
}
PUSHMI_INLINE_VAR constexpr struct pipe_fn {
#if __cpp_fold_expressions >= 201603
template <class T, class... FN>
auto operator()(T t, FN... fn) const -> decltype((t | ... | fn)) {
return (t | ... | fn);
auto operator()(T&& t, FN&&... fn) const
-> decltype(((T &&) t | ... | (FN &&) fn)) {
return ((T &&) t | ... | (FN &&) fn);
}
#else
template <class T, class F>
auto operator()(T t, F f) const -> decltype(t | f) {
return t | f;
auto operator()(T&& t, F&& f) const -> decltype((T &&) t | (F &&) f) {
return (T &&) t | (F &&) f;
}
template <class T, class F, class... FN, class This = pipe_fn>
auto operator()(T t, F f, FN... fn) const
-> decltype(This()((t | f), fn...)) {
return This()((t | f), fn...);
auto operator()(T&& t, F&& f, FN&&... fn) const
-> decltype(This()(((T &&) t | (F &&) f), (FN &&) fn...)) {
return This()(((T &&) t | (F &&) f), (FN &&) fn...);
}
#endif
} const pipe{};
......
......@@ -30,14 +30,28 @@ namespace pushmi {
template <class T>
using __property_category_t = typename T::property_category;
template <class T, class Void=void>
struct member_property_category {};
template <class T>
struct member_property_category<
T,
void_t<__property_category_t<T>>> {
using property_category = __property_category_t<T>;
};
// allow specializations to use enable_if to constrain
template <class T, class>
struct property_traits {};
template <class T, class Void>
struct property_traits : std::conditional_t<
std::is_same<T, std::decay_t<T>>::value,
member_property_category<T>,
property_traits<std::decay_t<T>, Void>>
{};
template <class T>
struct property_traits<
T,
void_t<__property_category_t<std::decay_t<T>>>> {
using property_category = __property_category_t<std::decay_t<T>>;
void_t<__property_category_t<T>>> {
using property_category = __property_category_t<T>;
};
template <class T>
......@@ -91,16 +105,24 @@ concept PropertySet,
template <class T>
using __properties_t = typename T::properties;
// allow specializations to use enable_if to constrain
template <class T, class>
struct property_set_traits {};
template <class T, class Void=void>
struct member_properties {};
template <class T>
struct property_set_traits<
struct member_properties<
T,
void_t<__properties_t<std::decay_t<T>>>> {
using properties = __properties_t<std::decay_t<T>>;
void_t<__properties_t<T>>> {
using properties = __properties_t<T>;
};
// allow specializations to use enable_if to constrain
template <class T, class Void>
struct property_set_traits : std::conditional_t<
std::is_same<T, std::decay_t<T>>::value,
member_properties<T>,
property_set_traits<std::decay_t<T>, Void>>
{};
template <class T>
using properties_t = std::enable_if_t<
PropertySet<__properties_t<property_set_traits<T>>>,
......@@ -193,7 +215,7 @@ struct category_query_impl : bool_<and_v<decltype(category_query_fn<ExpectedN>(
template <class PS, class... ExpectedN>
struct category_query : std::conditional_t<
Properties<PS> && not Or<Property<ExpectedN>...>,
Properties<PS> && !Or<Property<ExpectedN>...>,
detail::category_query_impl<PS, ExpectedN...>,
std::false_type> {};
......
......@@ -24,6 +24,7 @@
#include <folly/experimental/pushmi/properties.h>
#include <folly/experimental/pushmi/traits.h>
#include <future>
#include <type_traits>
namespace folly {
namespace pushmi {
......@@ -33,7 +34,7 @@ class any_receiver {
bool done_ = false;
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::promise<int>)]; // can hold a std::promise in-situ
std::aligned_union_t<0, std::promise<int>> buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() noexcept {
......@@ -65,7 +66,7 @@ class any_receiver {
ReceiveError<Wrapped, std::exception_ptr>,
"Wrapped receiver must support std::exception_ptr and be noexcept");
static_assert(
NothrowInvocable<decltype(::folly::pushmi::set_error), Wrapped, E>,
NothrowInvocable<decltype(::folly::pushmi::set_error), Wrapped&, E>,
"Wrapped receiver must support E and be noexcept");
}
template <class Wrapped>
......@@ -95,22 +96,22 @@ class any_receiver {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
new (&dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static void done(data& src) {
set_done(*static_cast<Wrapped*>((void*)src.buffer_));
set_done(*static_cast<Wrapped*>((void*)&src.buffer_));
}
static void error(data& src, E e) noexcept {
set_error(*static_cast<Wrapped*>((void*)src.buffer_), std::move(e));
set_error(*static_cast<Wrapped*>((void*)&src.buffer_), std::move(e));
}
static void value(data& src, VN... vn) {
set_value(*static_cast<Wrapped*>((void*)src.buffer_), std::move(vn)...);
set_value(*static_cast<Wrapped*>((void*)&src.buffer_), std::move(vn)...);
}
};
static const vtable vtbl{s::op, s::done, s::error, s::value};
new ((void*)data_.buffer_) Wrapped(std::move(obj));
new ((void*)&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
......@@ -137,16 +138,22 @@ class any_receiver {
new ((void*)this) any_receiver(std::move(that));
return *this;
}
void value(VN&&... vn) {
PUSHMI_TEMPLATE(class... AN)
(requires sizeof...(VN) == sizeof...(AN)) //
void value(AN&&... vn) {
// moved this check out of the requires due to a
// mismached pack size between VN and AN on gcc.
static_assert(And<Constructible<VN, AN>...>, "arguments must be convertible");
if (!done_) {
// done_ = true;
vptr_->value_(data_, (VN &&) vn...);
vptr_->value_(data_, VN{(AN&&)vn}...);
}
}
void error(E e) noexcept {
template<class A>
void error(A&& e) noexcept {
if (!done_) {
done_ = true;
vptr_->error_(data_, std::move(e));
vptr_->error_(data_, E{(A&&)e});
}
}
void done() {
......@@ -198,21 +205,20 @@ class receiver<VF, EF, DF> {
df_(std::move(df)) {}
PUSHMI_TEMPLATE(class... VN)
(requires Invocable<VF&, VN...>)
(requires Invocable<VF&, VN...>) //
void value(VN&&... vn) {
if (done_) {
return;
}
// done_ = true;
vf_((VN &&) vn...);
}
PUSHMI_TEMPLATE(class E)
(requires Invocable<EF&, E>)
void error(E e) noexcept {
static_assert(NothrowInvocable<EF&, E>, "error function must be noexcept");
(requires Invocable<EF&, E&&>) //
void error(E&& e) noexcept {
static_assert(NothrowInvocable<EF&, E&&>, "error function must be noexcept");
if (!done_) {
done_ = true;
ef_(std::move(e));
ef_((E&&)e);
}
}
void done() {
......@@ -244,12 +250,9 @@ class receiver<Data, DVF, DEF, DDF> {
static_assert(
!detail::is_v<DEF, on_value_fn>,
"the second parameter is the error implementation, but on_value{} was passed");
static_assert(
Invocable<DEF, Data&, std::exception_ptr>,
"error function must support std::exception_ptr");
static_assert(
NothrowInvocable<DEF, Data&, std::exception_ptr>,
"error function must be noexcept");
"error function must be noexcept and support std::exception_ptr");
public:
using properties =
......@@ -269,21 +272,20 @@ class receiver<Data, DVF, DEF, DDF> {
}
PUSHMI_TEMPLATE(class... VN)
(requires Invocable<DVF&, Data&, VN...>)
(requires Invocable<DVF&, Data&, VN...>) //
void value(VN&&... vn) {
if (!done_) {
// done_ = true;
vf_(data_, (VN &&) vn...);
}
}
PUSHMI_TEMPLATE(class E)
(requires Invocable<DEF&, Data&, E>)
void error(E e) noexcept {
(requires Invocable<DEF&, Data&, E>) //
void error(E&& e) noexcept {
static_assert(
NothrowInvocable<DEF&, Data&, E>, "error function must be noexcept");
NothrowInvocable<DEF&, Data&, E&&>, "error function must be noexcept");
if (!done_) {
done_ = true;
ef_(data_, std::move(e));
ef_(data_, (E&&)e);
}
}
void done() {
......@@ -496,18 +498,18 @@ struct construct_deduced<receiver> : make_receiver_fn {};
PUSHMI_TEMPLATE (class T, class In)
(requires SenderTo<In, std::promise<T>, is_single<>>)
std::future<T> future_from(In in) {
std::future<T> future_from(In&& in) {
std::promise<T> p;
auto result = p.get_future();
submit(in, std::move(p));
submit((In&&)in, std::move(p));
return result;
}
PUSHMI_TEMPLATE (class In)
(requires SenderTo<In, std::promise<void>, is_single<>>)
std::future<void> future_from(In in) {
std::future<void> future_from(In&& in) {
std::promise<void> p;
auto result = p.get_future();
submit(in, std::move(p));
submit((In&&)in, std::move(p));
return result;
}
......
......@@ -18,6 +18,7 @@
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/receiver.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <type_traits>
namespace folly {
namespace pushmi {
......@@ -26,7 +27,7 @@ template <class E, class... VN>
class any_single_sender {
union data {
void* pobj_ = nullptr;
char buffer_[sizeof(std::tuple<VN...>)]; // can hold a V in-situ
std::aligned_union_t<0, std::tuple<VN...>> buffer_;
} data_{};
template <class Wrapped>
static constexpr bool insitu() {
......@@ -35,58 +36,82 @@ class any_single_sender {
}
struct vtable {
static void s_op(data&, data*) {}
static any_executor<E> s_executor(data&) {
return {};
}
static void s_submit(data&, any_receiver<E, VN...>) {}
void (*op_)(data&, data*) = vtable::s_op;
any_executor<E> (*executor_)(data&) = vtable::s_executor;
void (*submit_)(data&, any_receiver<E, VN...>) = vtable::s_submit;
};
static constexpr vtable const noop_{};
vtable const* vptr_ = &noop_;
template <class Wrapped>
any_single_sender(Wrapped obj, std::false_type) : any_single_sender() {
any_single_sender(Wrapped obj, std::true_type, std::false_type) : any_single_sender() {
struct s {
static void op(data& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static any_executor<E> executor(data& src) {
return any_executor<E>{
::folly::pushmi::executor(*static_cast<Wrapped*>(src.pobj_))};
static void submit(data& src, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
std::move(*static_cast<Wrapped*>(src.pobj_)), std::move(out));
}
};
static const vtable vtbl{s::op, s::submit};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_single_sender(Wrapped obj, std::false_type, std::false_type) : any_single_sender() {
struct s {
static void op(data& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
delete static_cast<Wrapped const*>(src.pobj_);
}
static void submit(data& src, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>(src.pobj_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
static const vtable vtbl{s::op, s::submit};
data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_single_sender(Wrapped obj, std::true_type) noexcept
any_single_sender(Wrapped obj, std::true_type, std::true_type) noexcept
: any_single_sender() {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
new (&dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static any_executor<E> executor(data& src) {
return any_executor<E>{::folly::pushmi::executor(
*static_cast<Wrapped*>((void*)src.buffer_))};
static void submit(data& src, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
std::move(*static_cast<Wrapped*>((void*)&src.buffer_)), std::move(out));
}
};
static const vtable vtbl{s::op, s::submit};
new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class Wrapped>
any_single_sender(Wrapped obj, std::false_type, std::true_type) noexcept
: any_single_sender() {
struct s {
static void op(data& src, data* dst) {
if (dst)
new (&dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)&src.buffer_)));
static_cast<Wrapped const*>((void*)&src.buffer_)->~Wrapped();
}
static void submit(data& src, any_receiver<E, VN...> out) {
::folly::pushmi::submit(
*static_cast<Wrapped*>((void*)src.buffer_), std::move(out));
*static_cast<Wrapped*>((void*)&src.buffer_), std::move(out));
}
};
static const vtable vtbl{s::op, s::executor, s::submit};
new (data_.buffer_) Wrapped(std::move(obj));
static const vtable vtbl{s::op, s::submit};
new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl;
}
template <class T, class U = std::decay_t<T>>
......@@ -103,8 +128,8 @@ class any_single_sender {
PUSHMI_TEMPLATE(class Wrapped) //
(requires SenderTo<wrapped_t<Wrapped>, any_receiver<E, VN...>>) //
explicit any_single_sender(Wrapped obj) noexcept(insitu<Wrapped>())
: any_single_sender{std::move(obj), bool_<insitu<Wrapped>()>{}} {}
explicit any_single_sender(Wrapped&& obj) noexcept(insitu<Wrapped>())
: any_single_sender{std::move(obj), std::is_rvalue_reference<Wrapped&&>{}, bool_<insitu<Wrapped>()>{}} {}
~any_single_sender() {
vptr_->op_(data_, nullptr);
}
......@@ -113,11 +138,10 @@ class any_single_sender {
new ((void*)this) any_single_sender(std::move(that));
return *this;
}
any_executor<E> executor() {
return vptr_->executor_(data_);
}
void submit(any_receiver<E, VN...> out) {
vptr_->submit_(data_, std::move(out));
PUSHMI_TEMPLATE(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) //
void submit(Out&& out) {
vptr_->submit_(data_, any_receiver<E, VN...>{(Out &&) out});
}
};
......@@ -126,40 +150,30 @@ template <class E, class... VN>
constexpr typename any_single_sender<E, VN...>::vtable const
any_single_sender<E, VN...>::noop_;
template <class SF, class EXF>
class single_sender<SF, EXF> {
template <class SF>
class single_sender<SF> {
SF sf_;
EXF exf_;
public:
using properties = property_set<is_sender<>, is_single<>>;
constexpr single_sender() = default;
constexpr explicit single_sender(SF sf) : sf_(std::move(sf)) {}
constexpr single_sender(SF sf, EXF exf)
: sf_(std::move(sf)), exf_(std::move(exf)) {}
// TODO(T36778706): Workaround issues with `auto` as a method return type in
// modular builds in older versions of clang.
invoke_result_t<EXF&> executor() {
return exf_();
}
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<SF&, Out>)) //
void submit(Out out) {
sf_(std::move(out));
void submit(Out&& out) {
sf_((Out&&)out);
}
};
template <
PUSHMI_TYPE_CONSTRAINT(Sender<is_single<>>) Data,
class DSF,
class DEXF>
class single_sender<Data, DSF, DEXF> {
class DSF>
class single_sender<Data, DSF> {
Data data_;
DSF sf_;
DEXF exf_;
public:
using properties = property_set_insert_t<
......@@ -170,22 +184,23 @@ class single_sender<Data, DSF, DEXF> {
constexpr explicit single_sender(Data data) : data_(std::move(data)) {}
constexpr single_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {}
constexpr single_sender(Data data, DSF sf, DEXF exf)
: data_(std::move(data)), sf_(std::move(sf)), exf_(std::move(exf)) {}
auto executor() {
return exf_(data_);
}
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&, Data&, Out>)) //
void submit(Out out) {
sf_(data_, std::move(out));
void submit(Out&& out) & {
sf_(data_, (Out&&)out);
}
PUSHMI_TEMPLATE(class Out)
(requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&, Data&&, Out>)) //
void submit(Out&& out) && {
sf_(std::move(data_), (Out&&)out);
}
};
template <>
class single_sender<> : public single_sender<ignoreSF, trampolineEXF> {
class single_sender<> : public single_sender<ignoreSF> {
public:
single_sender() = default;
};
......@@ -194,72 +209,47 @@ class single_sender<> : public single_sender<ignoreSF, trampolineEXF> {
// make_single_sender
PUSHMI_INLINE_VAR constexpr struct make_single_sender_fn {
inline auto operator()() const {
return single_sender<ignoreSF, trampolineEXF>{};
return single_sender<ignoreSF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not Sender<SF>)) //
auto
operator()(SF sf) const {
return single_sender<SF, trampolineEXF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<>&& Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(
&&not Sender<SF>)) //
auto
operator()(SF sf, EXF exf) const {
return single_sender<SF, EXF>{std::move(sf), std::move(exf)};
return single_sender<SF>{std::move(sf)};
}
PUSHMI_TEMPLATE(class Data)
(requires True<>&& Sender<Data, is_single<>>) //
auto
operator()(Data d) const {
return single_sender<Data, passDSF, passDEXF>{std::move(d)};
return single_sender<Data, passDSF>{std::move(d)};
}
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_single<>>) //
auto
operator()(Data d, DSF sf) const {
return single_sender<Data, DSF, passDEXF>{std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_single<>>&& Invocable<DEXF&, Data&>) //
auto
operator()(Data d, DSF sf, DEXF exf) const {
return single_sender<Data, DSF, DEXF>{
std::move(d), std::move(sf), std::move(exf)};
return single_sender<Data, DSF>{std::move(d), std::move(sf)};
}
} const make_single_sender{};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
single_sender()->single_sender<ignoreSF, trampolineEXF>;
single_sender()->single_sender<ignoreSF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&&not Sender<SF>)) //
single_sender(SF)
->single_sender<SF, trampolineEXF>;
PUSHMI_TEMPLATE(class SF, class EXF)
(requires True<>&& Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(
&&not Sender<SF>)) //
single_sender(SF, EXF)
->single_sender<SF, EXF>;
->single_sender<SF>;
PUSHMI_TEMPLATE(class Data)
(requires True<>&& Sender<Data, is_single<>>) //
single_sender(Data)
->single_sender<Data, passDSF, passDEXF>;
->single_sender<Data, passDSF>;
PUSHMI_TEMPLATE(class Data, class DSF)
(requires Sender<Data, is_single<>>) //
single_sender(Data, DSF)
->single_sender<Data, DSF, passDEXF>;
PUSHMI_TEMPLATE(class Data, class DSF, class DEXF)
(requires Sender<Data, is_single<>>&& Invocable<DEXF&, Data&>) //
single_sender(Data, DSF, DEXF)
->single_sender<Data, DSF, DEXF>;
->single_sender<Data, DSF>;
#endif
template <>
......
......@@ -23,10 +23,10 @@
namespace folly {
namespace pushmi {
template <class E, class Executor>
template <class E, class Exec>
class strand_executor;
template <class E, class Executor>
template <class E, class Exec>
struct strand_queue_receiver;
template <class E>
......@@ -80,22 +80,22 @@ class strand_queue_base
virtual void dispatch() = 0;
};
template <class E, class Executor>
template <class E, class Exec>
class strand_queue : public strand_queue_base<E> {
public:
~strand_queue() {}
strand_queue(Executor ex) : ex_(std::move(ex)) {}
Executor ex_;
strand_queue(Exec ex) : ex_(std::move(ex)) {}
Exec ex_;
void dispatch() override;
auto shared_from_that() {
return std::static_pointer_cast<strand_queue<E, Executor>>(
return std::static_pointer_cast<strand_queue<E, Exec>>(
this->shared_from_this());
}
template <class Exec>
void value(Exec&&) {
template <class SubExec>
void value(SubExec&&) {
//
// pull ready items from the queue in order.
......@@ -114,7 +114,7 @@ class strand_queue : public strand_queue_base<E> {
this->remaining_ = this->items_.size();
auto that = shared_from_that();
auto subEx = strand_executor<E, Executor>{that};
auto subEx = strand_executor<E, Exec>{that};
while (!this->items_.empty() && --this->remaining_ >= 0) {
auto item{std::move(this->front())};
......@@ -152,22 +152,21 @@ class strand_queue : public strand_queue_base<E> {
}
auto that = shared_from_that();
submit(ex_, strand_queue_receiver<E, Executor>{that});
submit(schedule(ex_), strand_queue_receiver<E, Exec>{that});
}
};
template <class E, class Executor>
struct strand_queue_receiver : std::shared_ptr<strand_queue<E, Executor>> {
template <class E, class Exec>
struct strand_queue_receiver : std::shared_ptr<strand_queue<E, Exec>> {
~strand_queue_receiver() {}
explicit strand_queue_receiver(
std::shared_ptr<strand_queue<E, Executor>> that)
: std::shared_ptr<strand_queue<E, Executor>>(that) {}
explicit strand_queue_receiver(std::shared_ptr<strand_queue<E, Exec>> that)
: std::shared_ptr<strand_queue<E, Exec>>(that) {}
using properties = property_set<is_receiver<>>;
};
template <class E, class Executor>
void strand_queue<E, Executor>::dispatch() {
submit(ex_, strand_queue_receiver<E, Executor>{shared_from_that()});
template <class E, class Exec>
void strand_queue<E, Exec>::dispatch() {
submit(schedule(ex_), strand_queue_receiver<E, Exec>{shared_from_that()});
}
//
......@@ -175,81 +174,79 @@ void strand_queue<E, Executor>::dispatch() {
// single_executor.
//
template <class E, class Executor>
class strand_executor {
std::shared_ptr<strand_queue<E, Executor>> queue_;
template <class E, class Exec>
class strand_executor;
template <class E, class Exec>
class strand_task {
std::shared_ptr<strand_queue<E, Exec>> queue_;
public:
using properties = property_set<
is_sender<>,
is_executor<>,
property_set_index_t<properties_t<Executor>, is_never_blocking<>>,
is_fifo_sequence<>,
property_set_index_t<properties_t<sender_t<Exec>>, is_never_blocking<>>,
is_single<>>;
strand_executor(std::shared_ptr<strand_queue<E, Executor>> queue)
strand_task(std::shared_ptr<strand_queue<E, Exec>> queue)
: queue_(std::move(queue)) {}
auto executor() {
return *this;
}
PUSHMI_TEMPLATE(class Out)
(requires ReceiveValue<Out, any_executor_ref<E>>&&
ReceiveError<Out, E>)void submit(Out out) {
(requires ReceiveValue<Out&, any_executor_ref<E>>&& ReceiveError<Out, E>) //
void submit(Out out) {
// queue for later
std::unique_lock<std::mutex> guard{queue_->lock_};
queue_->items_.push(any_receiver<E, any_executor_ref<E>>{std::move(out)});
if (queue_->remaining_ == 0) {
// noone is minding the shop, send a worker
guard.unlock();
::folly::pushmi::submit(
queue_->ex_, strand_queue_receiver<E, Executor>{queue_});
::folly::pushmi::schedule(queue_->ex_), strand_queue_receiver<E, Exec>{queue_});
}
}
};
//
// the strand executor factory produces a new fifo ordered queue each time that
// it is called.
//
template <class E, class ExecutorFactory>
class strand_executor_factory_fn {
ExecutorFactory ef_;
template <class E, class Exec>
class strand_executor {
std::shared_ptr<strand_queue<E, Exec>> queue_;
public:
explicit strand_executor_factory_fn(ExecutorFactory ef)
: ef_(std::move(ef)) {}
auto operator()() const {
auto ex = ef_();
auto queue = std::make_shared<strand_queue<E, decltype(ex)>>(std::move(ex));
return strand_executor<E, decltype(ex)>{queue};
using properties = property_set<is_executor<>, is_fifo_sequence<>>;
strand_executor(std::shared_ptr<strand_queue<E, Exec>> queue)
: queue_(std::move(queue)) {}
strand_task<E, Exec> schedule() {
return {queue_};
}
};
template <class Exec>
class same_executor_factory_fn {
//
// the strand executor factory produces a new fifo ordered queue each time that
// it is called.
//
template <class E, class Exec>
class same_strand_factory_fn {
Exec ex_;
public:
explicit same_executor_factory_fn(Exec ex) : ex_(std::move(ex)) {}
auto operator()() const {
return ex_;
explicit same_strand_factory_fn(Exec ex) : ex_(std::move(ex)) {}
auto make_strand() const {
auto queue = std::make_shared<strand_queue<E, Exec>>(ex_);
return strand_executor<E, Exec>{queue};
}
};
PUSHMI_TEMPLATE(class E = std::exception_ptr, class ExecutorFactory)
(requires Invocable<ExecutorFactory&>&&
Executor<invoke_result_t<ExecutorFactory&>>&&
ConcurrentSequence<invoke_result_t<ExecutorFactory&>>) //
auto strands(ExecutorFactory ef) {
return strand_executor_factory_fn<E, ExecutorFactory>{std::move(ef)};
PUSHMI_TEMPLATE(class E = std::exception_ptr, class Provider)
(requires ExecutorProvider<Provider>&&
ConcurrentSequence<executor_t<Provider>>) //
auto strands(Provider ep) {
return same_strand_factory_fn<E, executor_t<Provider>>{get_executor(ep)};
}
PUSHMI_TEMPLATE(class E = std::exception_ptr, class Exec)
(requires Executor<Exec>&& ConcurrentSequence<Exec>) //
auto strands(Exec ex) {
return strand_executor_factory_fn<E, same_executor_factory_fn<Exec>>{
same_executor_factory_fn<Exec>{std::move(ex)}};
return same_strand_factory_fn<E, Exec>{std::move(ex)};
}
} // namespace pushmi
......
......@@ -18,7 +18,6 @@
#include <vector>
#include <folly/experimental/pushmi/concepts.h>
#include <folly/experimental/pushmi/time_single_sender.h>
#include <folly/experimental/pushmi/trampoline.h>
#include <folly/experimental/pushmi/detail/opt.h>
#include <folly/experimental/pushmi/o/extension_operators.h>
......@@ -43,25 +42,25 @@ struct subject<PS, TN...> {
std::vector<receiver_t> receivers_;
std::mutex lock_;
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)
(requires ReceiveError<Out, std::exception_ptr> && ReceiveValue<Out, TN...>)
void submit(Out out) {
std::unique_lock<std::mutex> guard(lock_);
if (ep_) {
set_error(out, ep_);
return;
}
if (!!t_) {
if (!!t_ && done_) {
auto args = *t_;
::folly::pushmi::apply(
::folly::pushmi::set_value,
std::tuple_cat(std::tuple<Out>{std::move(out)}, std::move(args)));
std::tuple_cat(std::tuple<Out&>{out}, std::move(args)));
return;
}
if (done_) {
set_done(out);
return;
}
receivers_.push_back(receiver_t{out});
receivers_.push_back(receiver_t{std::move(out)});
}
PUSHMI_TEMPLATE(class... VN)
(requires And<SemiMovable<VN>...>)
......@@ -74,7 +73,6 @@ struct subject<PS, TN...> {
out, detail::as_const(vn)...});
}
t_ = std::make_tuple((VN &&) vn...);
receivers_.clear();
}
PUSHMI_TEMPLATE(class E)
(requires SemiMovable<E>)
......@@ -118,9 +116,6 @@ struct subject<PS, TN...> {
std::shared_ptr<subject_shared> s = std::make_shared<subject_shared>();
auto executor() {
return trampoline();
}
PUSHMI_TEMPLATE(class Out)
(requires Receiver<Out>)
void submit(Out out) {
......
......@@ -219,16 +219,13 @@ void single_sender_test() {
static_assert(mi::Sender<decltype(in0)>, "in0 not a sender");
auto in1 = mi::MAKE(single_sender)(mi::ignoreSF{});
static_assert(mi::Sender<decltype(in1)>, "in1 not a sender");
auto in2 = mi::MAKE(single_sender)(mi::ignoreSF{}, mi::trampolineEXF{});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto in3 = mi::MAKE(single_sender)(
auto in2 = mi::MAKE(single_sender)(
[&](auto out) {
in0.submit(mi::MAKE(receiver)(
std::move(out),
mi::on_value([](auto d, int v) { mi::set_value(d, v); })));
},
[]() { return mi::trampoline(); });
static_assert(mi::Sender<decltype(in3)>, "in3 not a sender");
});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
std::promise<int> p0;
auto promise0 = mi::MAKE(receiver)(std::move(p0));
......@@ -237,13 +234,9 @@ void single_sender_test() {
auto out0 = mi::MAKE(receiver)();
auto out1 = mi::MAKE(receiver)(
out0, mi::on_value([](auto d, int v) { mi::set_value(d, v); }));
in3.submit(out1);
in2.submit(out1);
auto any0 = mi::any_single_sender<std::exception_ptr, int>(in0);
static_assert(
mi::Executor<mi::executor_t<decltype(in0)>>,
"sender has invalid executor");
}
void many_sender_test() {
......@@ -251,117 +244,20 @@ void many_sender_test() {
static_assert(mi::Sender<decltype(in0)>, "in0 not a sender");
auto in1 = mi::MAKE(many_sender)(mi::ignoreSF{});
static_assert(mi::Sender<decltype(in1)>, "in1 not a sender");
auto in2 = mi::MAKE(many_sender)(mi::ignoreSF{}, mi::trampolineEXF{});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto in3 = mi::MAKE(many_sender)(
auto in2 = mi::MAKE(many_sender)(
[&](auto out) {
in0.submit(mi::MAKE(receiver)(
std::move(out),
mi::on_value([](auto d, int v) { mi::set_value(d, v); })));
},
[]() { return mi::trampoline(); });
static_assert(mi::Sender<decltype(in3)>, "in3 not a sender");
auto out0 = mi::MAKE(receiver)();
auto out1 = mi::MAKE(receiver)(
out0, mi::on_value([](auto d, int v) { mi::set_value(d, v); }));
in3.submit(out1);
auto any0 = mi::any_many_sender<std::exception_ptr, int>(in0);
static_assert(
mi::Executor<mi::executor_t<decltype(in0)>>,
"sender has invalid executor");
}
void constrained_single_sender_test() {
auto in0 = mi::MAKE(constrained_single_sender)();
static_assert(mi::Sender<decltype(in0)>, "in0 not a sender");
auto in1 = mi::MAKE(constrained_single_sender)(mi::ignoreSF{});
static_assert(mi::Sender<decltype(in1)>, "in1 not a sender");
auto in2 = mi::MAKE(constrained_single_sender)(
mi::ignoreSF{}, mi::inlineConstrainedEXF{}, mi::priorityZeroF{});
});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto in3 = mi::MAKE(constrained_single_sender)(
[&](auto c, auto out) {
in0.submit(
c,
mi::MAKE(receiver)(std::move(out), mi::on_value([](auto d, int v) {
mi::set_value(d, v);
})));
},
[]() { return mi::inline_constrained_executor(); },
[]() { return 0; });
static_assert(mi::Sender<decltype(in3)>, "in3 not a sender");
auto in4 = mi::MAKE(constrained_single_sender)(
mi::ignoreSF{}, mi::inlineConstrainedEXF{});
static_assert(mi::Sender<decltype(in4)>, "in4 not a sender");
std::promise<int> p0;
auto promise0 = mi::MAKE(receiver)(std::move(p0));
in0.submit(in0.top(), std::move(promise0));
auto out0 = mi::MAKE(receiver)();
auto out1 = mi::MAKE(receiver)(
out0, mi::on_value([](auto d, int v) { mi::set_value(d, v); }));
in3.submit(in0.top(), out1);
auto any0 = mi::
any_constrained_single_sender<std::exception_ptr, std::ptrdiff_t, int>(
in0);
static_assert(
mi::Executor<mi::executor_t<decltype(in0)>>,
"sender has invalid executor");
in3 | op::submit();
in3 | op::blocking_submit();
}
in2.submit(out1);
void time_single_sender_test() {
auto in0 = mi::MAKE(time_single_sender)();
static_assert(mi::Sender<decltype(in0)>, "in0 not a sender");
auto in1 = mi::MAKE(time_single_sender)(mi::ignoreSF{});
static_assert(mi::Sender<decltype(in1)>, "in1 not a sender");
auto in2 = mi::MAKE(time_single_sender)(
mi::ignoreSF{}, mi::inlineTimeEXF{}, mi::systemNowF{});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto in3 = mi::MAKE(time_single_sender)(
[&](auto tp, auto out) {
in0.submit(
tp,
mi::MAKE(receiver)(std::move(out), mi::on_value([](auto d, int v) {
mi::set_value(d, v);
})));
},
[]() { return mi::inline_time_executor(); },
[]() { return std::chrono::system_clock::now(); });
static_assert(mi::Sender<decltype(in3)>, "in3 not a sender");
auto in4 = mi::MAKE(time_single_sender)(mi::ignoreSF{}, mi::inlineTimeEXF{});
static_assert(mi::Sender<decltype(in4)>, "in4 not a sender");
std::promise<int> p0;
auto promise0 = mi::MAKE(receiver)(std::move(p0));
in0.submit(in0.top(), std::move(promise0));
auto out0 = mi::MAKE(receiver)();
auto out1 = mi::MAKE(receiver)(
out0, mi::on_value([](auto d, int v) { mi::set_value(d, v); }));
in3.submit(in0.top(), out1);
auto any0 = mi::any_time_single_sender<
std::exception_ptr,
std::chrono::system_clock::time_point,
int>(in0);
static_assert(
mi::Executor<mi::executor_t<decltype(in0)>>,
"sender has invalid executor");
in3 | op::submit();
in3 | op::blocking_submit();
in3 | op::submit_at(in3.top() + 1s);
in3 | op::submit_after(1s);
auto any0 = mi::any_many_sender<std::exception_ptr, int>(in0);
}
void flow_receiver_1_test() {
......@@ -511,29 +407,22 @@ void flow_single_sender_test() {
static_assert(mi::Sender<decltype(in0)>, "in0 not a sender");
auto in1 = mi::MAKE(flow_single_sender)(mi::ignoreSF{});
static_assert(mi::Sender<decltype(in1)>, "in1 not a sender");
auto in2 = mi::MAKE(flow_single_sender)(mi::ignoreSF{}, mi::trampolineEXF{});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto in3 = mi::MAKE(flow_single_sender)(
auto in2 = mi::MAKE(flow_single_sender)(
[&](auto out) {
in0.submit(mi::MAKE(flow_receiver)(
std::move(out),
mi::on_value([](auto d, int v) { mi::set_value(d, v); })));
},
[]() { return mi::trampoline(); });
static_assert(mi::Sender<decltype(in3)>, "in3 not a sender");
});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto out0 = mi::MAKE(flow_receiver)();
auto out1 = mi::MAKE(flow_receiver)(
out0, mi::on_value([](auto d, int v) { mi::set_value(d, v); }));
in3.submit(out1);
in2.submit(out1);
auto any0 =
mi::any_flow_single_sender<std::exception_ptr, std::exception_ptr, int>(
in0);
static_assert(
mi::Executor<mi::executor_t<decltype(in0)>>,
"sender has invalid executor");
}
void flow_many_sender_test() {
......@@ -541,31 +430,24 @@ void flow_many_sender_test() {
static_assert(mi::Sender<decltype(in0)>, "in0 not a sender");
auto in1 = mi::MAKE(flow_many_sender)(mi::ignoreSF{});
static_assert(mi::Sender<decltype(in1)>, "in1 not a sender");
auto in2 = mi::MAKE(flow_many_sender)(mi::ignoreSF{}, mi::trampolineEXF{});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto in3 = mi::MAKE(flow_many_sender)(
auto in2 = mi::MAKE(flow_many_sender)(
[&](auto out) {
in0.submit(mi::MAKE(flow_receiver)(
std::move(out),
mi::on_value([](auto d, int v) { mi::set_value(d, v); })));
},
[]() { return mi::trampoline(); });
static_assert(mi::Sender<decltype(in3)>, "in3 not a sender");
});
static_assert(mi::Sender<decltype(in2)>, "in2 not a sender");
auto out0 = mi::MAKE(flow_receiver)();
auto out1 = mi::MAKE(flow_receiver)(
out0, mi::on_value([](auto d, int v) { mi::set_value(d, v); }));
in3.submit(out1);
in2.submit(out1);
auto any0 = mi::any_flow_many_sender<
std::exception_ptr,
std::ptrdiff_t,
std::exception_ptr,
int>(in0);
static_assert(
mi::Executor<mi::executor_t<decltype(in0)>>,
"sender has invalid executor");
}
TEST(CompileTest, Test) {}
......@@ -101,6 +101,16 @@ class ImmediateFlowManySender : public Test {
int signals_{0};
};
TEST(AnyFlowManySender, Construct) {
std::array<int, 3> arr{{0, 9, 99}};
auto m = folly::pushmi::operators::flow_from(arr);
auto any_m = folly::pushmi::any_flow_many_sender<
std::exception_ptr,
std::ptrdiff_t,
std::exception_ptr,
int>(m);
}
TEST_F(ImmediateFlowManySender, EarlyCancellation) {
make_producer() | op::submit(make_consumer([](auto up) {
// immediately stop producer
......@@ -124,7 +134,8 @@ TEST_F(ImmediateFlowManySender, LateCancellation) {
using NT = decltype(mi::new_thread());
inline auto make_time(mi::time_source<>& t, NT& ex) {
return t.make(mi::systemNowF{}, [ex]() { return ex; })();
auto strands = t.make(mi::systemNowF{}, ex);
return mi::make_strand(strands);
}
class ConcurrentFlowManySender : public Test {
......@@ -170,7 +181,9 @@ class ConcurrentFlowManySender : public Test {
return;
}
// submit work to happen later
data.p->tnt | op::submit_at(at_, [p = data.p](auto) {
data.p->tnt |
op::schedule_at(at_) |
op::submit([p = data.p](auto) {
// check boolean to select signal
if (!p->stop) {
::mi::set_value(p->out, 42);
......@@ -182,6 +195,7 @@ class ConcurrentFlowManySender : public Test {
signals_ += 100000;
data.p->stop.store(true);
data.p->tnt |
op::schedule() |
op::submit([p = data.p](auto) { ::mi::set_done(p->out); });
++cancel_;
},
......@@ -189,11 +203,14 @@ class ConcurrentFlowManySender : public Test {
signals_ += 10000;
data.p->stop.store(true);
data.p->tnt |
op::schedule() |
op::submit([p = data.p](auto) { ::mi::set_done(p->out); });
++cancel_;
});
tnt_ | op::submit([p, sup = std::move(up)](auto) mutable {
tnt_ |
op::schedule() |
op::submit([p, sup = std::move(up)](auto) mutable {
// pass reference for cancellation.
::mi::set_starting(p->out, std::move(sup));
});
......@@ -213,7 +230,9 @@ class ConcurrentFlowManySender : public Test {
mi::on_starting([&, at](auto up) {
signals_ += 10;
mi::set_value(up, 1);
tcncl_ | op::submit_at(at, [up = std::move(up)](auto) mutable {
tcncl_ |
op::schedule_at(at) |
op::submit([up = std::move(up)](auto) mutable {
::mi::set_done(up);
});
})));
......
......@@ -132,7 +132,8 @@ TEST_F(ImmediateFlowSingleSender, LateCancellation) {
using NT = decltype(mi::new_thread());
inline auto make_time(mi::time_source<>& t, NT& ex) {
return t.make(mi::systemNowF{}, [ex]() { return ex; })();
auto strands = t.make(mi::systemNowF{}, ex);
return mi::make_strand(strands);
}
class ConcurrentFlowSingleSender : public Test {
......@@ -185,6 +186,7 @@ class ConcurrentFlowSingleSender : public Test {
// make all the signals come from the same thread
tnt_ |
op::schedule() |
op::submit([&,
stoppee = std::move(tokens.first),
up_not_a_shadow_howtoeven = std::move(up),
......@@ -194,8 +196,8 @@ class ConcurrentFlowSingleSender : public Test {
// submit work to happen later
tnt |
op::submit_at(
at_,
op::schedule_at(at_) |
op::submit(
[stoppee = std::move(stoppee),
out = std::move(out)](auto) mutable {
// check boolean to select signal
......@@ -225,7 +227,7 @@ class ConcurrentFlowSingleSender : public Test {
// stop producer before it is scheduled to run
mi::on_starting([&, at](auto up) {
signals_ += 10;
tcncl_ | op::submit_at(at, [up = std::move(up)](auto) mutable {
tcncl_ | op::schedule_at(at) | op::submit([up = std::move(up)](auto) mutable {
::mi::set_done(up);
});
}));
......
......@@ -50,7 +50,7 @@ struct countdownsingle {
template <class ExecutorRef>
void operator()(ExecutorRef exec) {
if (--*counter > 0) {
exec | op::submit(*this);
exec | op::schedule() | op::submit(*this);
}
}
};
......@@ -58,7 +58,8 @@ struct countdownsingle {
using NT = decltype(mi::new_thread());
inline auto make_time(mi::time_source<>& t, NT& ex) {
return t.make(mi::systemNowF{}, [ex]() { return ex; })();
auto strands = t.make(mi::systemNowF{}, ex);
return mi::make_strand(strands);
}
class NewthreadExecutor : public Test {
......@@ -79,7 +80,7 @@ TEST_F(NewthreadExecutor, BlockingSubmitNow) {
auto signals = 0;
auto start = v::now(tnt_);
auto signaled = start;
tnt_ | op::transform([](auto tnt) { return tnt | ep::now(); }) |
tnt_ | op::schedule() | op::transform([](auto tnt) { return tnt | ep::now(); }) |
op::blocking_submit(
[&](auto at) {
signaled = at;
......@@ -98,7 +99,7 @@ TEST_F(NewthreadExecutor, BlockingSubmitNow) {
TEST_F(NewthreadExecutor, BlockingGetNow) {
auto start = v::now(tnt_);
auto signaled = tnt_ | op::transform([](auto tnt) { return v::now(tnt); }) |
auto signaled = tnt_ | op::schedule() | op::transform([](auto tnt) { return v::now(tnt); }) |
op::get<std::chrono::system_clock::time_point>;
auto delay =
......@@ -117,11 +118,12 @@ TEST_F(NewthreadExecutor, SubmissionsAreOrderedInTime) {
++pushed;
});
};
tnt_ | op::submit(v::on_value([push](auto tnt) {
tnt_ | op::schedule() | op::submit(v::on_value([push](auto tnt) {
auto now = tnt | ep::now();
tnt | op::submit_after(40ms, push(40)) |
op::submit_at(now + 10ms, push(10)) | op::submit_after(20ms, push(20)) |
op::submit_at(now + 10ms, push(11));
tnt | op::schedule_after(40ms) | op::submit(push(40));
tnt | op::schedule_at(now + 10ms) | op::submit(push(10));
tnt | op::schedule_after(20ms) | op::submit(push(20));
tnt | op::schedule_at(now + 10ms) | op::submit(push(11));
}));
while (pushed.load() < 4) {
......@@ -135,7 +137,7 @@ TEST_F(NewthreadExecutor, SubmissionsAreOrderedInTime) {
TEST_F(NewthreadExecutor, NowIsCalled) {
bool done = false;
tnt_ | ep::now();
tnt_ | op::blocking_submit([&](auto tnt) {
tnt_ | op::schedule() | op::blocking_submit([&](auto tnt) {
tnt | ep::now();
done = true;
});
......@@ -145,7 +147,7 @@ TEST_F(NewthreadExecutor, NowIsCalled) {
TEST_F(NewthreadExecutor, BlockingSubmit) {
auto signals = 0;
nt_ | op::transform([](auto) { return 42; }) |
nt_ | op::schedule() | op::transform([](auto) { return 42; }) |
op::blocking_submit(
[&](auto) { signals += 100; },
[&](auto) noexcept { signals += 1000; },
......@@ -156,7 +158,7 @@ TEST_F(NewthreadExecutor, BlockingSubmit) {
}
TEST_F(NewthreadExecutor, BlockingGet) {
auto v = nt_ | op::transform([](auto) { return 42; }) | op::get<int>;
auto v = nt_ | op::schedule() | op::transform([](auto) { return 42; }) | op::get<int>;
EXPECT_THAT(v, Eq(42)) << "expected that the result would be different";
}
......@@ -167,9 +169,9 @@ TEST_F(NewthreadExecutor, VirtualDerecursion) {
recurse = [&](::folly::pushmi::any_executor_ref<> nt) {
if (--counter <= 0)
return;
nt | op::submit(recurse);
nt | op::schedule() | op::submit(recurse);
};
nt_ | op::blocking_submit([&](auto nt) { recurse(nt); });
nt_ | op::schedule() | op::blocking_submit([&](auto nt) { recurse(nt); });
EXPECT_THAT(counter, Eq(0))
<< "expected that all nested submissions complete";
......@@ -178,7 +180,7 @@ TEST_F(NewthreadExecutor, VirtualDerecursion) {
TEST_F(NewthreadExecutor, StaticDerecursion) {
int counter = 100'000;
countdownsingle single{counter};
nt_ | op::blocking_submit(single);
nt_ | op::schedule() | op::blocking_submit(single);
EXPECT_THAT(counter, Eq(0))
<< "expected that all nested submissions complete";
......@@ -194,7 +196,7 @@ TEST_F(NewthreadExecutor, UsedWithOn) {
::folly::pushmi::set_value(out, std::numeric_limits<int8_t>::min());
::folly::pushmi::set_value(out, std::numeric_limits<int8_t>::max());
});
sender | op::on([&]() { return nt_; }) |
sender | op::on(mi::strands(nt_)) |
op::blocking_submit(v::on_value(
[&](auto v) { values.push_back(folly::to<std::string>(v)); }));
......
......@@ -24,14 +24,10 @@ using namespace std::literals;
#include <folly/experimental/pushmi/o/extension_operators.h>
#include <folly/experimental/pushmi/o/from.h>
#include <folly/experimental/pushmi/o/just.h>
#include <folly/experimental/pushmi/o/on.h>
#include <folly/experimental/pushmi/o/submit.h>
#include <folly/experimental/pushmi/o/tap.h>
#include <folly/experimental/pushmi/o/transform.h>
#include <folly/experimental/pushmi/new_thread.h>
#include <folly/experimental/pushmi/trampoline.h>
using namespace folly::pushmi::aliases;
#include <folly/portability/GMock.h>
......@@ -39,39 +35,12 @@ using namespace folly::pushmi::aliases;
using namespace testing;
TEST(EmptyNoArgSingleSender, TapAndSubmit) {
TEST(EmptySingleSender, TapAndSubmit) {
auto e = op::empty();
using E = decltype(e);
EXPECT_THAT((v::SenderTo<E, v::any_receiver<>, v::is_single<>>), Eq(true))
<< "expected empty to return a single sender that can take an any_receiver";
int signals = 0;
e |
op::tap(
[&]() { signals += 100; },
[&](auto) noexcept { signals += 1000; },
[&]() { signals += 10; }) |
op::submit(
[&]() { signals += 100; },
[&](auto) noexcept { signals += 1000; },
[&]() { signals += 10; });
EXPECT_THAT(signals, Eq(20))
<< "expected the done signal to be recorded twice";
EXPECT_THROW(v::future_from(e).get(), std::future_error)
<< "expected future_error when future_from is applied";
EXPECT_THAT(
(std::is_same<std::future<void>, decltype(v::future_from(e))>::value),
Eq(true))
<< "expected future_from(e) to return std::future<void>";
}
TEST(EmptyIntSingleSender, TapAndSubmit) {
auto e = op::empty<int>();
using E = decltype(e);
<< "expected empty to return a single sender that can take an any_receiver<>";
EXPECT_THAT(
(v::SenderTo<
......@@ -84,22 +53,22 @@ TEST(EmptyIntSingleSender, TapAndSubmit) {
int signals = 0;
e |
op::tap(
[&](auto) { signals += 100; },
[&]() { signals += 100; },
[&](auto) noexcept { signals += 1000; },
[&]() { signals += 10; }) |
op::submit(
[&](auto) { signals += 100; },
[&]() { signals += 100; },
[&](auto) noexcept { signals += 1000; },
[&]() { signals += 10; });
EXPECT_THAT(signals, Eq(20))
<< "expected the done signal to be recorded twice";
EXPECT_THROW(v::future_from<int>(e).get(), std::future_error)
EXPECT_THROW(v::future_from(e).get(), std::future_error)
<< "expected future_error when future_from is applied";
EXPECT_THAT(
(std::is_same<std::future<int>, decltype(v::future_from<int>(e))>::value),
(std::is_same<std::future<void>, decltype(v::future_from(e))>::value),
Eq(true))
<< "expected future_from(e) to return std::future<void>";
}
......
......@@ -49,7 +49,7 @@ struct countdownsingle {
template <class ExecutorRef>
void operator()(ExecutorRef exec) {
if (--*counter > 0) {
exec | op::submit(*this);
exec | op::schedule() | op::submit(*this);
}
}
};
......@@ -63,7 +63,7 @@ class TrampolineExecutor : public Test {
TEST_F(TrampolineExecutor, TransformAndSubmit) {
auto signals = 0;
tr_ | op::transform([](auto) { return 42; }) |
tr_ | op::schedule() | op::transform([](auto) { return 42; }) |
op::submit(
[&](auto) { signals += 100; },
[&](auto) noexcept { signals += 1000; },
......@@ -74,7 +74,7 @@ TEST_F(TrampolineExecutor, TransformAndSubmit) {
}
TEST_F(TrampolineExecutor, BlockingGet) {
auto v = tr_ | op::transform([](auto) { return 42; }) | op::get<int>;
auto v = tr_ | op::schedule() | op::transform([](auto) { return 42; }) | op::get<int>;
EXPECT_THAT(v, Eq(42)) << "expected that the result would be different";
}
......@@ -85,9 +85,9 @@ TEST_F(TrampolineExecutor, VirtualDerecursion) {
recurse = [&](::folly::pushmi::any_executor_ref<> tr) {
if (--counter <= 0)
return;
tr | op::submit(recurse);
tr | op::schedule() | op::submit(recurse);
};
tr_ | op::submit([&](auto exec) { recurse(exec); });
tr_ | op::schedule() | op::submit([&](auto exec) { recurse(exec); });
EXPECT_THAT(counter, Eq(0))
<< "expected that all nested submissions complete";
......@@ -96,7 +96,7 @@ TEST_F(TrampolineExecutor, VirtualDerecursion) {
TEST_F(TrampolineExecutor, StaticDerecursion) {
int counter = 100'000;
countdownsingle single{counter};
tr_ | op::submit(single);
tr_ | op::schedule() | op::submit(single);
EXPECT_THAT(counter, Eq(0))
<< "expected that all nested submissions complete";
......@@ -119,13 +119,6 @@ TEST_F(TrampolineExecutor, UsedWithOn) {
EXPECT_THAT(values, ElementsAre(folly::to<std::string>(2.0)))
<< "expected that only the first item was pushed";
EXPECT_THAT(
(std::is_same<
mi::executor_t<decltype(sender)>,
mi::executor_t<decltype(inlineon)>>::value),
Eq(true))
<< "expected that executor was not changed by on";
}
TEST_F(TrampolineExecutor, UsedWithVia) {
......@@ -145,11 +138,4 @@ TEST_F(TrampolineExecutor, UsedWithVia) {
EXPECT_THAT(values, ElementsAre(folly::to<std::string>(2.0)))
<< "expected that only the first item was pushed";
EXPECT_THAT(
(!std::is_same<
mi::executor_t<decltype(sender)>,
mi::executor_t<decltype(inlinevia)>>::value),
Eq(true))
<< "expected that executor was changed by via";
}
/*
* Copyright 2018-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/experimental/pushmi/receiver.h>
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/inline.h>
#include <folly/experimental/pushmi/constrained_single_sender.h>
namespace folly {
namespace pushmi {
template <class E, class TP, class... VN>
class any_time_single_sender
: public any_constrained_single_sender<E, TP, VN...> {
public:
using properties = property_set<is_time<>, is_single<>>;
constexpr any_time_single_sender() = default;
template <class T>
constexpr explicit any_time_single_sender(T t)
: any_constrained_single_sender<E, TP, VN...>(std::move(t)) {}
template <class T0, class T1, class... TN>
constexpr any_time_single_sender(T0 t0, T1 t1, TN... tn)
: any_constrained_single_sender<E, TP, VN...>(
std::move(t0),
std::move(t1),
std::move(tn)...) {}
any_time_executor<E, TP> executor() {
return any_time_executor<E, TP>{
any_constrained_single_sender<E, TP, VN...>::executor()};
}
};
template <class SF, class NF, class EXF>
class time_single_sender<SF, NF, EXF>
: public constrained_single_sender<SF, NF, EXF> {
public:
using properties = property_set<is_time<>, is_single<>>;
constexpr time_single_sender() = default;
template <class T>
constexpr explicit time_single_sender(T t)
: constrained_single_sender<SF, NF, EXF>(std::move(t)) {}
template <class T0, class T1, class... TN>
constexpr time_single_sender(T0 t0, T1 t1, TN... tn)
: constrained_single_sender<SF, NF, EXF>(
std::move(t0),
std::move(t1),
std::move(tn)...) {}
};
template <PUSHMI_TYPE_CONSTRAINT(SemiMovable)... TN>
class time_single_sender : public constrained_single_sender<TN...> {
public:
constexpr time_single_sender() = default;
template <class T>
constexpr explicit time_single_sender(T t)
: constrained_single_sender<TN...>(std::move(t)) {}
template <class C0, class C1, class... CN>
constexpr time_single_sender(C0 c0, C1 c1, CN... cn)
: constrained_single_sender<TN...>(
std::move(c0),
std::move(c1),
std::move(cn)...) {}
};
template <>
class time_single_sender<>
: public time_single_sender<ignoreSF, systemNowF, inlineTimeEXF> {
public:
time_single_sender() = default;
};
////////////////////////////////////////////////////////////////////////////////
// make_time_single_sender
PUSHMI_INLINE_VAR constexpr struct make_time_single_sender_fn {
inline auto operator()() const {
return time_single_sender<ignoreSF, systemNowF, inlineTimeEXF>{};
}
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf) const {
return time_single_sender<SF, systemNowF, inlineTimeEXF>{std::move(sf)};
}
PUSHMI_TEMPLATE (class SF, class EXF)
(requires Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf, EXF exf) const {
return time_single_sender<SF, systemNowF, EXF>{
std::move(sf), std::move(exf)};
}
PUSHMI_TEMPLATE (class SF, class NF, class EXF)
(requires Invocable<NF&> && Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
auto operator()(SF sf, EXF exf, NF nf) const {
return time_single_sender<SF, NF, EXF>{
std::move(sf), std::move(exf), std::move(nf)};
}
PUSHMI_TEMPLATE (class Data)
(requires TimeSender<Data, is_single<>>)
auto operator()(Data d) const {
return time_single_sender<Data, passDSF, passDNF, passDEXF>{std::move(d)};
}
PUSHMI_TEMPLATE (class Data, class DSF)
(requires TimeSender<Data, is_single<>>)
auto operator()(Data d, DSF sf) const {
return time_single_sender<Data, DSF, passDNF, passDEXF>{
std::move(d), std::move(sf)};
}
PUSHMI_TEMPLATE (class Data, class DSF, class DEXF)
(requires TimeSender<Data, is_single<>> && Invocable<DEXF&, Data&>)
auto operator()(Data d, DSF sf, DEXF exf) const {
return time_single_sender<Data, DSF, passDNF, DEXF>{
std::move(d), std::move(sf), std::move(exf)};
}
PUSHMI_TEMPLATE (class Data, class DSF, class DNF, class DEXF)
(requires TimeSender<Data, is_single<>> && Invocable<DNF&, Data&> &&
Invocable<DEXF&, Data&>)
auto operator()(Data d, DSF sf, DEXF exf, DNF nf) const {
return time_single_sender<Data, DSF, DNF, DEXF>{
std::move(d), std::move(sf), std::move(exf), std::move(nf)};
}
} const make_time_single_sender {};
////////////////////////////////////////////////////////////////////////////////
// deduction guides
#if __cpp_deduction_guides >= 201703
time_single_sender() -> time_single_sender<ignoreSF, systemNowF, inlineTimeEXF>;
PUSHMI_TEMPLATE(class SF)
(requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
time_single_sender(SF) -> time_single_sender<SF, systemNowF, inlineTimeEXF>;
PUSHMI_TEMPLATE (class SF, class EXF)
(requires Invocable<EXF&> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
time_single_sender(SF, EXF) -> time_single_sender<SF, systemNowF, EXF>;
PUSHMI_TEMPLATE (class SF, class NF, class EXF)
(requires Invocable<NF&> && Invocable<EXF&>
PUSHMI_BROKEN_SUBSUMPTION(&& not Sender<SF>))
time_single_sender(SF, EXF, NF) -> time_single_sender<SF, NF, EXF>;
PUSHMI_TEMPLATE (class Data, class DSF)
(requires TimeSender<Data, is_single<>>)
time_single_sender(Data, DSF) ->
time_single_sender<Data, DSF, passDNF, passDEXF>;
PUSHMI_TEMPLATE (class Data, class DSF, class DEXF)
(requires TimeSender<Data, is_single<>> && Invocable<DEXF&, Data&>)
time_single_sender(Data, DSF, DEXF) ->
time_single_sender<Data, DSF, passDNF, DEXF>;
PUSHMI_TEMPLATE (class Data, class DSF, class DNF, class DEXF)
(requires TimeSender<Data, is_single<>> && Invocable<DNF&, Data&> &&
Invocable<DEXF&, Data&>)
time_single_sender(Data, DSF, DEXF, DNF) ->
time_single_sender<Data, DSF, DNF, DEXF>;
#endif
template<>
struct construct_deduced<time_single_sender>
: make_time_single_sender_fn {};
} // namespace pushmi
} // namespace folly
......@@ -17,7 +17,6 @@
#include <folly/experimental/pushmi/detail/opt.h>
#include <folly/experimental/pushmi/executor.h>
#include <folly/experimental/pushmi/time_single_sender.h>
#include <algorithm>
#include <queue>
......@@ -32,7 +31,7 @@ namespace pushmi {
template <class E, class TP>
class time_source_shared;
template <class E, class TP, class NF, class Executor>
template <class E, class TP, class NF, class Exec>
class time_source_executor;
template <class E, class TP>
......@@ -104,7 +103,7 @@ class time_source_queue_base
virtual void dispatch() = 0;
};
template <class E, class TP, class NF, class Executor>
template <class E, class TP, class NF, class Exec>
class time_source_queue : public time_source_queue_base<E, TP> {
public:
using time_point = std::decay_t<TP>;
......@@ -112,25 +111,25 @@ class time_source_queue : public time_source_queue_base<E, TP> {
time_source_queue(
std::weak_ptr<time_source_shared<E, time_point>> source,
NF nf,
Executor ex)
Exec ex)
: source_(std::move(source)), nf_(std::move(nf)), ex_(std::move(ex)) {}
std::weak_ptr<time_source_shared<E, time_point>> source_;
NF nf_;
Executor ex_;
Exec ex_;
void dispatch() override;
auto shared_from_that() {
return std::static_pointer_cast<time_source_queue<E, TP, NF, Executor>>(
return std::static_pointer_cast<time_source_queue<E, TP, NF, Exec>>(
this->shared_from_this());
}
template <class Exec>
void value(Exec&&) {
template <class SubExec>
void value(SubExec&&) {
auto s = source_.lock();
if (s->t_.get_id() == std::this_thread::get_id()) {
// Executor is not allowed to use the time_source thread
// Exec is not allowed to use the time_source thread
std::terminate();
}
......@@ -151,7 +150,7 @@ class time_source_queue : public time_source_queue_base<E, TP> {
return;
}
auto that = shared_from_that();
auto subEx = time_source_executor<E, TP, NF, Executor>{s, that};
auto subEx = time_source_executor<E, TP, NF, Exec>{s, that};
while (!this->heap_.empty() && this->heap_.top().when <= start) {
auto item{std::move(this->top())};
this->heap_.pop();
......@@ -224,22 +223,22 @@ class time_source_queue : public time_source_queue_base<E, TP> {
}
};
template <class E, class TP, class NF, class Executor>
template <class E, class TP, class NF, class Exec>
struct time_source_queue_receiver
: std::shared_ptr<time_source_queue<E, TP, NF, Executor>> {
: std::shared_ptr<time_source_queue<E, TP, NF, Exec>> {
~time_source_queue_receiver() {}
explicit time_source_queue_receiver(
std::shared_ptr<time_source_queue<E, TP, NF, Executor>> that)
: std::shared_ptr<time_source_queue<E, TP, NF, Executor>>(that),
std::shared_ptr<time_source_queue<E, TP, NF, Exec>> that)
: std::shared_ptr<time_source_queue<E, TP, NF, Exec>>(that),
source_(that->source_.lock()) {}
using properties = property_set<is_receiver<>>;
std::shared_ptr<time_source_shared<E, TP>> source_;
};
template <class E, class TP, class NF, class Executor>
void time_source_queue<E, TP, NF, Executor>::dispatch() {
submit(
ex_, time_source_queue_receiver<E, TP, NF, Executor>{shared_from_that()});
template <class E, class TP, class NF, class Exec>
void time_source_queue<E, TP, NF, Exec>::dispatch() {
::folly::pushmi::submit(
::folly::pushmi::schedule(ex_), time_source_queue_receiver<E, TP, NF, Exec>{shared_from_that()});
}
template <class E, class TP>
......@@ -435,45 +434,70 @@ class time_source_shared : public time_source_shared_base<E, TP> {
}
};
template <class E, class TP, class NF, class Exec>
class time_source_executor;
//
// the time executor will queue the work to the time ordered heap.
// the time task will queue the work to the time ordered heap.
//
template <class E, class TP, class NF, class Executor>
template <class E, class TP, class NF, class Exec>
class time_source_task {
using time_point = std::decay_t<TP>;
time_point tp_;
std::shared_ptr<time_source_shared<E, time_point>> source_;
std::shared_ptr<time_source_queue<E, time_point, NF, Exec>> queue_;
public:
using properties = property_set_insert_t<properties_t<sender_t<Exec>>, property_set<
is_never_blocking<>>>;
time_source_task(
time_point tp,
std::shared_ptr<time_source_shared<E, time_point>> source,
std::shared_ptr<time_source_queue<E, time_point, NF, Exec>> queue)
: tp_(tp), source_(std::move(source)), queue_(std::move(queue)) {}
PUSHMI_TEMPLATE(class Out)
(requires ReceiveValue<Out, any_time_executor_ref<E, TP>>&&
ReceiveError<Out, E>)
void submit(Out&& out) && {
// queue for later
source_->insert(
queue_,
time_heap_item<E, TP>{
tp_, any_receiver<E, any_time_executor_ref<E, TP>>{(Out&&)out}});
}
};
//
// the time task will queue create tasks.
//
template <class E, class TP, class NF, class Exec>
class time_source_executor {
using time_point = std::decay_t<TP>;
std::shared_ptr<time_source_shared<E, time_point>> source_;
std::shared_ptr<time_source_queue<E, time_point, NF, Executor>> queue_;
std::shared_ptr<time_source_queue<E, time_point, NF, Exec>> queue_;
public:
using properties = property_set<
is_time<>,
is_executor<>,
is_maybe_blocking<>,
is_fifo_sequence<>,
is_single<>>;
using properties = property_set<is_time<>,
is_fifo_sequence<>>;
time_source_executor(
std::shared_ptr<time_source_shared<E, time_point>> source,
std::shared_ptr<time_source_queue<E, time_point, NF, Executor>> queue)
std::shared_ptr<time_source_queue<E, time_point, NF, Exec>> queue)
: source_(std::move(source)), queue_(std::move(queue)) {}
auto top() {
TP top() {
return queue_->nf_();
}
auto executor() {
return *this;
}
PUSHMI_TEMPLATE(class TPA, class Out)
(requires Regular<TPA>&& ReceiveValue<Out, any_time_executor_ref<E, TP>>&&
ReceiveError<Out, E>)
void submit(TPA tp, Out out) {
// queue for later
source_->insert(
queue_,
time_heap_item<E, TP>{
tp, any_receiver<E, any_time_executor_ref<E, TP>>{std::move(out)}});
time_source_task<E, TP, NF, Exec> schedule() {
return {queue_->nf_(), source_, queue_};
}
time_source_task<E, TP, NF, Exec> schedule(TP tp) {
return {tp, source_, queue_};
}
};
......@@ -482,21 +506,21 @@ class time_source_executor {
// is called.
//
template <class E, class TP, class NF, class ExecutorFactory>
template <class E, class TP, class NF, class Factory>
class time_source_executor_factory_fn {
using time_point = std::decay_t<TP>;
std::shared_ptr<time_source_shared<E, time_point>> source_;
NF nf_;
ExecutorFactory ef_;
Factory ef_;
public:
time_source_executor_factory_fn(
std::shared_ptr<time_source_shared<E, time_point>> source,
NF nf,
ExecutorFactory ef)
Factory ef)
: source_(std::move(source)), nf_(std::move(nf)), ef_(std::move(ef)) {}
auto operator()() {
auto ex = ef_();
auto make_strand() {
auto ex = ::folly::pushmi::make_strand(ef_);
auto queue =
std::make_shared<time_source_queue<E, time_point, NF, decltype(ex)>>(
source_, nf_, std::move(ex));
......@@ -505,6 +529,25 @@ class time_source_executor_factory_fn {
}
};
template <class E, class TP, class NF, class Exec>
class time_source_same_executor_factory_fn {
using time_point = std::decay_t<TP>;
std::shared_ptr<time_source_shared<E, time_point>> source_;
std::shared_ptr<time_source_queue<E, time_point, NF, Exec>> queue_;
NF nf_;
public:
time_source_same_executor_factory_fn(
std::shared_ptr<time_source_shared<E, time_point>> source,
std::shared_ptr<time_source_queue<E, time_point, NF, Exec>> queue,
NF nf)
: source_(std::move(source)), queue_(std::move(queue)), nf_(std::move(nf)) {}
auto make_strand() {
return time_source_executor<E, time_point, NF, Exec>{source_,
queue_};
}
};
//
// each time_source is an independent source of timed events
//
......@@ -539,15 +582,33 @@ class time_source {
source_->start(source_);
}
PUSHMI_TEMPLATE(class NF, class ExecutorFactory)
(requires Invocable<ExecutorFactory&>&&
Executor<invoke_result_t<ExecutorFactory&>>&&
NeverBlocking<invoke_result_t<
ExecutorFactory&>>)
auto make(NF nf, ExecutorFactory ef) {
return time_source_executor_factory_fn<E, time_point, NF, ExecutorFactory>{
PUSHMI_TEMPLATE(class NF, class Factory)
(requires StrandFactory<Factory> && not Executor<Factory> && not ExecutorProvider<Factory>) //
auto make(NF nf, Factory ef) {
return time_source_executor_factory_fn<E, time_point, NF, Factory>{
source_, std::move(nf), std::move(ef)};
}
PUSHMI_TEMPLATE(class NF, class Provider)
(requires ExecutorProvider<Provider>&&
NeverBlocking<sender_t<executor_t<Provider>>> && not StrandFactory<Provider>) //
auto make(NF nf, Provider ep) {
auto ex = ::folly::pushmi::get_executor(ep);
auto queue =
std::make_shared<time_source_queue<E, time_point, NF, decltype(ex)>>(
source_, nf, std::move(ex));
return time_source_same_executor_factory_fn<E, time_point, NF, decltype(ex)>{
source_, std::move(queue), std::move(nf)};
}
PUSHMI_TEMPLATE(class NF, class Exec)
(requires Executor<Exec>&&
NeverBlocking<sender_t<Exec>> && not StrandFactory<Exec>) //
auto make(NF nf, Exec ex) {
auto queue =
std::make_shared<time_source_queue<E, time_point, NF, Exec>>(
source_, nf, std::move(ex));
return time_source_same_executor_factory_fn<E, time_point, NF, Exec>{
source_, std::move(queue), std::move(nf)};
}
void join() {
source_->join(source_);
......
......@@ -15,6 +15,7 @@
*/
#pragma once
#include <folly/experimental/pushmi/piping.h>
#include <folly/experimental/pushmi/executor.h>
#include <algorithm>
#include <chrono>
......@@ -27,8 +28,6 @@ namespace pushmi {
struct recurse_t {};
constexpr const recurse_t recurse{};
struct _pipeable_sender_ {};
namespace detail {
PUSHMI_INLINE_VAR constexpr struct ownordelegate_t {
......@@ -49,47 +48,39 @@ class trampoline_id {
template <class E = std::exception_ptr>
class trampoline;
template <class E = std::exception_ptr>
class delegator : _pipeable_sender_ {
public:
template<class E, class Tag>
struct trampoline_task {
using properties = property_set<
is_sender<>,
is_executor<>,
is_maybe_blocking<>,
is_fifo_sequence<>,
is_single<>>;
delegator executor() {
return {};
}
PUSHMI_TEMPLATE(class SingleReceiver)
(requires ReceiveValue<
remove_cvref_t<SingleReceiver>,
SingleReceiver,
any_executor_ref<E>>)
void submit(SingleReceiver&& what) {
trampoline<E>::submit(ownordelegate, std::forward<SingleReceiver>(what));
void submit(SingleReceiver&& what) && {
trampoline<E>::submit(Tag{}, std::forward<SingleReceiver>(what));
}
};
template <class E = std::exception_ptr>
class nester : _pipeable_sender_ {
class delegator : pipeorigin {
public:
using properties = property_set<
is_sender<>,
is_executor<>,
is_maybe_blocking<>,
is_fifo_sequence<>,
is_single<>>;
using properties = property_set<is_executor<>, is_fifo_sequence<>>;
nester executor() {
trampoline_task<E, ownordelegate_t> schedule() {
return {};
}
PUSHMI_TEMPLATE(class SingleReceiver)
(requires ReceiveValue<
remove_cvref_t<SingleReceiver>,
any_executor_ref<E>>)
void submit(SingleReceiver&& what) {
trampoline<E>::submit(ownornest, std::forward<SingleReceiver>(what));
};
template <class E = std::exception_ptr>
class nester : pipeorigin {
public:
using properties = property_set<is_executor<>, is_fifo_sequence<>>;
trampoline_task<E, ownornest_t> schedule() {
return {};
}
};
......@@ -97,7 +88,7 @@ template <class E>
class trampoline {
private:
using error_type = std::decay_t<E>;
using work_type = any_receiver<error_type, any_executor_ref<error_type>>;
using work_type = any_receiver<error_type>;
using queue_type = std::deque<work_type>;
using pending_type = std::tuple<int, queue_type, bool>;
......@@ -127,6 +118,14 @@ class trampoline {
return owner() != nullptr;
}
struct delegate_impl {
template<class Data>
void operator()(Data& d){
delegator<E> that;
set_value(d, that);
}
};
template <class Selector, class Derived>
static void submit(Selector, Derived&, recurse_t) {
if (!is_owned()) {
......@@ -149,7 +148,7 @@ class trampoline {
try {
if (++depth(*owner()) > 100) {
// defer work to owner
pending(*owner()).push_back(work_type{std::move(awhat)});
pending(*owner()).push_back(work_type{make_receiver(std::move(awhat), delegate_impl{})});
} else {
// dynamic recursion - optimization to balance queueing and
// stack usage and value interleaving on the same thread.
......@@ -226,7 +225,7 @@ class trampoline {
go = repeat(pending_store);
}
} else {
pending(pending_store).push_back(work_type{std::move(awhat)});
pending(pending_store).push_back(work_type{make_receiver(std::move(awhat), delegate_impl{})});
}
if (pending(pending_store).empty()) {
......@@ -236,7 +235,7 @@ class trampoline {
while (!pending(pending_store).empty()) {
auto what = std::move(pending(pending_store).front());
pending(pending_store).pop_front();
set_value(what, any_executor_ref<error_type>{that});
set_value(what);
set_done(what);
}
}
......
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