Commit 00fba105 authored by Dominik Charousset's avatar Dominik Charousset

Add third decorator for splitting requests

parent a4e4c4ff
......@@ -81,6 +81,7 @@ set (LIBCAF_CORE_SRCS
src/serializer.cpp
src/sequencer.cpp
src/shared_spinlock.cpp
src/splitter.cpp
src/sync_request_bouncer.cpp
src/try_match.cpp
src/type_erased_value.cpp
......
......@@ -161,6 +161,8 @@ public:
return compare(invalid_actor);
}
static actor splice_impl(std::initializer_list<actor> xs);
/// @endcond
private:
......@@ -184,6 +186,12 @@ private:
/// Combine `f` and `g` so that `(f*g)(x) = f(g(x))`.
actor operator*(actor f, actor g);
/// @relates actor
template <class... Ts>
actor splice(const actor& x, const actor& y, const Ts&... zs) {
return actor::splice_impl({x, y, zs...});
}
/// @relates actor
void serialize(serializer&, actor&, const unsigned int);
......
......@@ -32,13 +32,13 @@ namespace caf {
template <class... Sigs, class... Ts>
void check_typed_input(const typed_actor<Sigs...>&,
const detail::type_list<Ts...>&) {
static_assert(detail::tl_find<
static_assert(detail::tl_index_of<
detail::type_list<Ts...>,
atom_value
>::value == -1,
"atom(...) notation is not sufficient for static type "
"checking, please use atom_constant instead in this context");
static_assert(detail::tl_find_if<
static_assert(detail::tl_exists<
detail::type_list<Sigs...>,
detail::input_is<detail::type_list<Ts...>>::template eval
>::value >= 0,
......
......@@ -113,7 +113,7 @@ public:
template <class T>
typename std::enable_if<std::is_arithmetic<T>::value>::type
apply(T& x) {
apply_builtin(static_cast<builtin>(detail::tl_find<builtin_t, T>::value),
apply_builtin(static_cast<builtin>(detail::tl_index_of<builtin_t, T>::value),
&x);
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DECORATOR_SPLITTER_HPP
#define CAF_DECORATOR_SPLITTER_HPP
#include <vector>
#include "caf/actor_addr.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/monitorable_actor.hpp"
namespace caf {
namespace decorator {
/// An actor decorator implementing "dot operator"-like compositions,
/// i.e., `f.g(x) = f(g(x))`. Composed actors are hidden actors.
/// A composed actor exits when either of its constituent actors exits;
/// Constituent actors have no dependency on the composed actor
/// by default, and exit of a composed actor has no effect on its
/// constituent actors. A composed actor is hosted on the same actor
/// system and node as `g`, the first actor on the forwarding chain.
class splitter : public monitorable_actor {
public:
using message_types_set = std::set<std::string>;
splitter(std::vector<actor_addr> workers, message_types_set msg_types);
// non-system messages are processed and then forwarded;
// system messages are handled and consumed on the spot;
// in either case, the processing is done synchronously
void enqueue(mailbox_element_ptr what, execution_unit* host) override;
message_types_set message_types() const override;
private:
std::vector<actor_addr> workers_;
message_types_set msg_types_;
};
} // namespace decorator
} // namespace caf
#endif // CAF_DECORATOR_SPLITTER_HPP
......@@ -17,8 +17,8 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_MPI_COMPOSITION_HPP
#define CAF_DETAIL_MPI_COMPOSITION_HPP
#ifndef CAF_DETAIL_MPI_SEQUENCER_HPP
#define CAF_DETAIL_MPI_SEQUENCER_HPP
#include "caf/replies_to.hpp"
......@@ -28,30 +28,30 @@ namespace caf {
namespace detail {
template <class X, class Y>
struct mpi_composition_one {
struct mpi_sequencer_one {
using type = void;
};
template <class... Xs, class... Ys, class... Zs>
struct mpi_composition_one<typed_mpi<type_list<Xs...>, type_list<Ys...>>,
typed_mpi<type_list<Ys...>, type_list<Zs...>>> {
struct mpi_sequencer_one<typed_mpi<type_list<Xs...>, type_list<Ys...>>,
typed_mpi<type_list<Ys...>, type_list<Zs...>>> {
using type = typed_mpi<type_list<Xs...>, type_list<Zs...>>;
};
template <class X, class Y>
struct mpi_composition_all;
struct mpi_sequencer_all;
template <class X, class... Ys>
struct mpi_composition_all<X, type_list<Ys...>> {
using type = type_list<typename mpi_composition_one<X, Ys>::type...>;
struct mpi_sequencer_all<X, type_list<Ys...>> {
using type = type_list<typename mpi_sequencer_one<X, Ys>::type...>;
};
template <template <class...> class Target, class Ys, class... Xs>
struct mpi_composition {
struct mpi_sequencer {
// combine each X with all Ys
using all =
typename tl_concat<
typename mpi_composition_all<Xs, Ys>::type...
typename mpi_sequencer_all<Xs, Ys>::type...
>::type;
// drop all mismatches (void results)
using filtered = typename tl_filter_not_type<all, void>::type;
......@@ -66,4 +66,4 @@ struct mpi_composition {
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_MPI_COMPOSITION_HPP
#endif // CAF_DETAIL_MPI_SEQUENCER_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_MPI_SPLICE_HPP
#define CAF_DETAIL_MPI_SPLICE_HPP
#include <type_traits>
#include "caf/replies_to.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/typed_actor_util.hpp"
namespace caf {
namespace detail {
template <class X, class... Ts>
struct mpi_splice_one;
template <class X>
struct mpi_splice_one<X> {
using type = X;
};
template <class X, class... Ts>
struct mpi_splice_one<X, none_t, Ts...> {
using type = none_t;
};
template <class... Xs, class... Ys, class... Zs, class... Ts>
struct mpi_splice_one<typed_mpi<type_list<Xs...>, type_list<Ys...>>,
typed_mpi<type_list<Xs...>, type_list<Zs...>>,
Ts...>
: mpi_splice_one<typed_mpi<type_list<Xs...>, type_list<Ys..., Zs...>>, Ts...> {
// nop
};
template <template <class...> class Target, class List, class... Lists>
struct mpi_splice;
template <template <class...> class Target, class... Ts, class... Lists>
struct mpi_splice<Target, type_list<Ts...>, Lists...> {
using spliced_list =
type_list<
typename mpi_splice_one<
Ts,
typename tl_find<
Lists,
input_is<typename Ts::input_types>::template eval
>::type...
>::type...
>;
using filtered_list =
typename tl_filter_not_type<
spliced_list,
none_t
>::type;
static_assert(tl_size<filtered_list>::value > 0,
"cannot splice incompatible actor handles");
using type = typename tl_apply<filtered_list, Target>::type;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_MPI_SPLICE_HPP
......@@ -28,7 +28,6 @@ struct tbind {
template <class Arg2>
struct type {
static constexpr bool value = Tpl<Arg1, Arg2>::value;
};
};
......
......@@ -25,6 +25,7 @@
#include <type_traits>
#include "caf/unit.hpp"
#include "caf/none.hpp"
#include "caf/detail/tbind.hpp"
#include "caf/detail/type_pair.hpp"
......@@ -302,16 +303,57 @@ struct tl_unzip<type_list<Elements...>> {
// int index_of(list, type)
template <class List, typename T>
struct tl_index_of {
static constexpr size_t value =
tl_index_of<typename tl_tail<List>::type, T>::value;
/// Finds the first element of type `What` beginning at index `Pos`.
template <int Pos, class X, class... Ts>
struct tl_index_of_impl;
template <int Pos, class X>
struct tl_index_of_impl<Pos, X> {
static constexpr int value = -1;
};
template <int Pos, class X, class... Ts>
struct tl_index_of_impl<Pos, X, X, Ts...> {
static constexpr int value = Pos;
};
template <int Pos, class X, class T, class... Ts>
struct tl_index_of_impl<Pos, X, T, Ts...> {
static constexpr int value = tl_index_of_impl<Pos + 1, X, Ts...>::value;
};
/// Finds the first element satisfying `Pred` beginning at index `Pos`.
template <class List, class T>
struct tl_index_of;
template <class... Ts, class T>
struct tl_index_of<type_list<Ts...>, T> {
static constexpr int value = tl_index_of_impl<0, T, Ts...>::value;
};
template <size_t N, typename T, class... Ts>
struct tl_index_of<
type_list<type_pair<std::integral_constant<size_t, N>, T>, Ts...>, T> {
static constexpr size_t value = N;
// int index_of(list, predicate)
template <int Pos, template <class> class Pred, class... Ts>
struct tl_index_where_impl;
template <int Pos, template <class> class Pred>
struct tl_index_where_impl<Pos, Pred> {
static constexpr int value = -1;
};
template <int Pos, template <class> class Pred, class T, class... Ts>
struct tl_index_where_impl<Pos, Pred, T, Ts...> {
static constexpr int value
= Pred<T>::value ? Pos : tl_index_where_impl<Pos + 1, Pred, Ts...>::value;
};
/// Finds the first element satisfying a given predicate.
template <class List, template <class> class Pred>
struct tl_index_where;
template <class... Ts, template <class> class Pred>
struct tl_index_where<type_list<Ts...>, Pred> {
static constexpr int value = tl_index_where_impl<0, Pred, Ts...>::value;
};
// list reverse()
......@@ -338,38 +380,31 @@ struct tl_reverse {
// bool find(list, type)
/// Finds the first element of type `What` beginning at index `Pos`.
template <class List, template <class> class Pred, int Pos = 0>
template <template <class> class Pred, class... Ts>
struct tl_find_impl;
template <template <class> class Pred, int Pos>
struct tl_find_impl<type_list<>, Pred, Pos> {
static constexpr int value = -1;
template <template <class> class Pred>
struct tl_find_impl<Pred> {
using type = none_t;
};
template <template <class> class Pred, int Pos, class T0, class... Ts>
struct tl_find_impl<type_list<T0, Ts...>, Pred, Pos> {
// always use type_list for recursive calls to minimize instantiation costs
static constexpr int value =
Pred<T0>::value ? Pos
: tl_find_impl<type_list<Ts...>, Pred, Pos + 1>::value;
template <template <class> class Pred, class T, class... Ts>
struct tl_find_impl<Pred, T, Ts...> {
using type =
typename std::conditional<
Pred<T>::value,
T,
typename tl_find_impl<Pred, Ts...>::type
>::type;
};
/// Finds the first element satisfying `Pred` beginning at
/// index `Pos`.
template <class List, template <class> class Pred, int Pos = 0>
struct tl_find_if {
static constexpr int value = tl_find_impl<List, Pred, Pos>::value;
};
/// Finds the first element satisfying `Pred` beginning at index `Pos`.
template <class List, template <class> class Pred>
struct tl_find;
/// Finds the first element of type `What` beginning at
/// index `Pos`.
template <class List, class What, int Pos = 0>
struct tl_find {
static constexpr int value = tl_find_impl<
List,
tbind<std::is_same, What>::template type,
Pos
>::value;
template <class... Ts, template <class> class Pred>
struct tl_find<type_list<Ts...>, Pred> {
using type = typename tl_find_impl<Pred, Ts...>::type;
};
// bool forall(predicate)
......
......@@ -94,7 +94,7 @@ using int_types_by_size =
template <class T, bool IsIntegral = std::is_integral<T>::value>
struct type_nr {
static constexpr uint16_t value =
static_cast<uint16_t>(tl_find<sorted_builtin_types, T>::value + 1);
static_cast<uint16_t>(tl_index_of<sorted_builtin_types, T>::value + 1);
};
template <class T>
......@@ -107,13 +107,13 @@ struct type_nr<T, true> {
typename tpair::second
>::type;
static constexpr uint16_t value =
static_cast<uint16_t>(tl_find<sorted_builtin_types, type>::value + 1);
static_cast<uint16_t>(tl_index_of<sorted_builtin_types, type>::value + 1);
};
template <>
struct type_nr<bool, true> {
static constexpr uint16_t value =
static_cast<uint16_t>(tl_find<sorted_builtin_types, bool>::value + 1);
static_cast<uint16_t>(tl_index_of<sorted_builtin_types, bool>::value + 1);
};
template <atom_value V>
......
......@@ -178,13 +178,13 @@ struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> {
template <class Signatures, class InputTypes>
struct deduce_output_type {
static constexpr int input_pos =
tl_find_if<
using signature =
typename tl_find<
Signatures,
input_is<InputTypes>::template eval
>::value;
static_assert(input_pos != -1, "typed actor does not support given input");
using signature = typename tl_at<Signatures, input_pos>::type;
>::type;
static_assert(! std::is_same<signature, none_t>::value,
"typed actor does not support given input");
using type = typename signature::output_types;
// generates the appropriate `delegated<...>` type from given signatures
using delegated_type = typename detail::tl_apply<type, delegated>::type;
......
......@@ -326,7 +326,7 @@ protected:
template <class Pattern>
struct pattern_has_wildcard {
static constexpr bool value =
detail::tl_find<
detail::tl_index_of<
Pattern,
anything
>::value != -1;
......
......@@ -267,6 +267,8 @@ public:
return size() == sizeof...(Ts) && match_elements_impl(p0, tlist);
}
message& operator+=(const message& x);
/// @cond PRIVATE
using raw_ptr = detail::message_data*;
......
......@@ -31,10 +31,12 @@
#include "caf/typed_response_promise.hpp"
#include "caf/decorator/adapter.hpp"
#include "caf/decorator/splitter.hpp"
#include "caf/decorator/sequencer.hpp"
#include "caf/detail/mpi_bind.hpp"
#include "caf/detail/mpi_composition.hpp"
#include "caf/detail/mpi_splice.hpp"
#include "caf/detail/mpi_sequencer.hpp"
namespace caf {
......@@ -287,24 +289,48 @@ bool operator!=(const typed_actor<Xs...>& x,
/// Returns a new actor that implements the composition `f.g(x) = f(g(x))`.
/// @relates typed_actor
template <class... Xs, class... Ys>
typename detail::mpi_composition<
typename detail::mpi_sequencer<
typed_actor,
detail::type_list<Xs...>,
Ys...
>::type
operator*(typed_actor<Xs...> f, typed_actor<Ys...> g) {
using result =
typename detail::mpi_composition<
typename detail::mpi_sequencer<
typed_actor,
detail::type_list<Xs...>,
Ys...
>::type;
if (! f || ! g)
return invalid_actor;
auto mts = g->home_system().message_types(result{});
auto ptr = make_counted<decorator::sequencer>(f.address(), g.address(),
std::move(mts));
return actor_cast<result>(std::move(ptr));
}
template <class... Xs, class... Ts>
typename detail::mpi_splice<
typed_actor,
detail::type_list<Xs...>,
typename Ts::signatures...
>::type
splice(const typed_actor<Xs...>& x, const Ts&... xs) {
using result =
typename detail::mpi_splice<
typed_actor,
detail::type_list<Xs...>,
typename Ts::signatures...
>::type;
std::vector<actor_addr> tmp{x.address(), xs.address()...};
for (auto& addr : tmp)
if (! addr)
return invalid_actor;
auto mts = x->home_system().message_types(result{});
auto ptr = make_counted<decorator::splitter>(std::move(tmp), std::move(mts));
return actor_cast<result>(std::move(ptr));
}
/// @relates typed_actor
template <class T, class... Ts>
typename std::enable_if<T::is_saving::value>::type
......
......@@ -200,12 +200,11 @@ private:
template <class U>
void set(U&& arg) {
using type = typename std::decay<U>::type;
static constexpr int type_id = detail::tl_find_if<
types,
detail::tbind<
is_same_ish, type
>::template type
>::value;
static constexpr int type_id =
detail::tl_index_where<
types,
detail::tbind<is_same_ish, type>::template type
>::value;
static_assert(type_id >= 0, "invalid type for variant");
std::integral_constant<int, type_id> token;
if (type_ != type_id) {
......@@ -248,8 +247,8 @@ private:
template <class T, class... Us>
T& get(variant<Us...>& value) {
using namespace detail;
int_token<tl_find_if<type_list<Us...>,
tbind<is_same_ish, T>::template type>::value> token;
int_token<tl_index_where<type_list<Us...>,
tbind<is_same_ish, T>::template type>::value> token;
// silence compiler error about "binding to unrelated types" such as
// 'signed char' to 'char' (which is obvious bullshit)
return reinterpret_cast<T&>(value.get(token));
......@@ -266,8 +265,8 @@ const T& get(const variant<Us...>& value) {
template <class T, class... Us>
T* get(variant<Us...>* value) {
using namespace detail;
int_token<tl_find_if<type_list<Us...>,
tbind<is_same_ish, T>::template type>::value> token;
int_token<tl_index_where<type_list<Us...>,
tbind<is_same_ish, T>::template type>::value> token;
if (value->is(token))
return &get<T>(*value);
return nullptr;
......
......@@ -31,6 +31,7 @@
#include "caf/event_based_actor.hpp"
#include "caf/decorator/adapter.hpp"
#include "caf/decorator/splitter.hpp"
#include "caf/decorator/sequencer.hpp"
namespace caf {
......@@ -106,4 +107,18 @@ std::string to_string(const actor& x) {
return to_string(x.address());
}
actor actor::splice_impl(std::initializer_list<actor> xs) {
if (xs.size() < 2)
return invalid_actor;
std::vector<actor_addr> tmp;
for (auto& x : xs)
if (x)
tmp.push_back(x.address());
else
return invalid_actor;
auto ptr = make_counted<decorator::splitter>(std::move(tmp),
std::set<std::string>{});
return actor_cast<actor>(std::move(ptr));
}
} // namespace caf
......@@ -69,6 +69,12 @@ bool message::match_element(size_t pos, uint16_t typenr,
return vals_->match_element(pos, typenr, rtti);
}
message& message::operator+=(const message& x) {
auto tmp = *this + x;
swap(tmp);
return *this;
}
bool message::equals(const message& other) const {
if (empty())
return other.empty();
......
......@@ -44,8 +44,7 @@ sequencer::sequencer(actor_addr f, actor_addr g, message_types_set msg_types)
g_->attach(default_attachable::make_monitor(address()));
}
void sequencer::enqueue(mailbox_element_ptr what,
execution_unit* context) {
void sequencer::enqueue(mailbox_element_ptr what, execution_unit* context) {
if (! what)
return; // not even an empty message
auto reason = exit_reason_.load();
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/decorator/splitter.hpp"
#include "caf/actor_system.hpp"
#include "caf/stateful_actor.hpp"
#include "caf/response_promise.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/default_attachable.hpp"
#include "caf/detail/disposer.hpp"
#include "caf/detail/sync_request_bouncer.hpp"
namespace caf {
namespace decorator {
namespace {
struct splitter_state {
response_promise rp;
message result;
size_t pending;
};
behavior fan_out_fan_in(stateful_actor<splitter_state>* self,
const std::vector<actor_addr>& workers) {
return {
others >> [=] {
self->state.rp = self->make_response_promise();
self->state.pending = workers.size();
// request().await() has LIFO ordering
for (auto i = workers.rbegin(); i != workers.rend(); ++i)
self->request(actor_cast<actor>(*i), self->current_message()).generic_await(
[=](const message& tmp) {
self->state.result += tmp;
if (--self->state.pending == 0)
self->state.rp.deliver(std::move(self->state.result));
},
[=](const error& err) {
self->state.rp.deliver(err);
self->quit();
}
);
self->unbecome();
}
};
}
} // namespace <anonymous>
splitter::splitter(std::vector<actor_addr> workers, message_types_set msg_types)
: monitorable_actor(&workers.front()->home_system(),
workers.front()->home_system().next_actor_id(),
workers.front()->node(),
is_abstract_actor_flag | is_actor_dot_decorator_flag),
workers_(std::move(workers)),
msg_types_(std::move(msg_types)) {
// composed actor has dependency on constituent actors by default;
// if either constituent actor is already dead upon establishing
// the dependency, the actor is spawned dead
auto addr = address();
for (auto& worker : workers_)
worker->attach(default_attachable::make_monitor(addr));
}
void splitter::enqueue(mailbox_element_ptr what, execution_unit* context) {
if (! what)
return; // not even an empty message
auto reason = exit_reason_.load();
if (reason != exit_reason::not_exited) {
// actor has exited
auto& mid = what->mid;
if (mid.is_request()) {
// make sure that a request always gets a response;
// the exit reason reflects the first actor on the
// forwarding chain that is out of service
detail::sync_request_bouncer rb{reason};
rb(what->sender, mid);
}
return;
}
auto down_msg_handler = [&](const down_msg& dm) {
// quit if either `f` or `g` are no longer available
auto pred = [&](const actor_addr& x) { return x == dm.source; };
if (std::any_of(workers_.begin(), workers_.end(), pred))
monitorable_actor::cleanup(dm.reason, context);
};
// handle and consume the system message;
// the only effect that MAY result from handling a system message
// is to exit the actor if it hasn't exited already;
// `handle_system_message()` is thread-safe, and if the actor
// has already exited upon the invocation, nothing is done
if (handle_system_message(*what, context, false, down_msg_handler))
return;
auto helper = context->system().spawn(fan_out_fan_in, workers_);
helper->enqueue(std::move(what), context);
}
splitter::message_types_set splitter::message_types() const {
return msg_types_;
}
} // namespace decorator
} // namespace caf
......@@ -28,7 +28,7 @@ using namespace caf;
namespace {
behavior dbl_bhvr(event_based_actor* self) {
behavior testee(event_based_actor* self) {
return {
[](int v) {
return 2 * v;
......@@ -65,7 +65,7 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(adapter_tests, fixture)
CAF_TEST(identity) {
auto dbl = system.spawn(dbl_bhvr);
auto dbl = system.spawn(testee);
CAF_CHECK(system.registry().running() == 1);
auto bound = dbl.bind(1);
CAF_CHECK(system.registry().running() == 1);
......@@ -80,7 +80,7 @@ CAF_TEST(identity) {
// bound actor spawned dead if decorated
// actor is already dead upon spawning
CAF_TEST(lifetime_1) {
auto dbl = system.spawn(dbl_bhvr);
auto dbl = system.spawn(testee);
self->monitor(dbl);
anon_send_exit(dbl, exit_reason::kill);
wait_until_exited();
......@@ -90,7 +90,7 @@ CAF_TEST(lifetime_1) {
// bound actor exits when decorated actor exits
CAF_TEST(lifetime_2) {
auto dbl = system.spawn(dbl_bhvr);
auto dbl = system.spawn(testee);
auto bound = dbl.bind(1);
self->monitor(bound);
anon_send(dbl, message{});
......@@ -101,13 +101,13 @@ CAF_TEST(lifetime_2) {
// 2) exits by receiving an exit message
// 3) exit has no effect on decorated actor
CAF_TEST(lifetime_3) {
auto dbl = system.spawn(dbl_bhvr);
auto dbl = system.spawn(testee);
auto bound = dbl.bind(1);
anon_send(bound, down_msg{self->address(),
exit_reason::kill});
CAF_CHECK(! exited(bound));
self->monitor(bound);
auto em_sender = system.spawn(dbl_bhvr);
auto em_sender = system.spawn(testee);
em_sender->link_to(bound->address());
anon_send_exit(em_sender, exit_reason::kill);
wait_until_exited();
......@@ -123,7 +123,7 @@ CAF_TEST(lifetime_3) {
}
CAF_TEST(request_response_promise) {
auto dbl = system.spawn(dbl_bhvr);
auto dbl = system.spawn(testee);
auto bound = dbl.bind(1);
anon_send_exit(bound, exit_reason::kill);
CAF_CHECK(exited(bound));
......@@ -171,7 +171,7 @@ CAF_TEST(partial_currying) {
}
CAF_TEST(full_currying) {
auto dbl_actor = system.spawn(dbl_bhvr);
auto dbl_actor = system.spawn(testee);
auto bound = dbl_actor.bind(1);
self->request(bound, message{}).receive(
[](int v) {
......
......@@ -28,7 +28,7 @@ using namespace caf;
namespace {
behavior dbl_bhvr(event_based_actor* self) {
behavior testee(event_based_actor* self) {
return {
[](int v) {
return 2 * v;
......@@ -42,13 +42,13 @@ behavior dbl_bhvr(event_based_actor* self) {
using first_stage = typed_actor<replies_to<int>::with<double, double>>;
using second_stage = typed_actor<replies_to<double, double>::with<double>>;
first_stage::behavior_type first_stage_impl() {
first_stage::behavior_type typed_first_stage() {
return [](int i) {
return std::make_tuple(i * 2.0, i * 4.0);
};
};
second_stage::behavior_type second_stage_impl() {
second_stage::behavior_type typed_second_stage() {
return [](double x, double y) {
return x * y;
};
......@@ -82,8 +82,8 @@ CAF_TEST_FIXTURE_SCOPE(sequencer_tests, fixture)
CAF_TEST(identity) {
actor_system system_of_g;
actor_system system_of_f;
auto g = system_of_g.spawn(first_stage_impl);
auto f = system_of_f.spawn(second_stage_impl);
auto g = system_of_g.spawn(typed_first_stage);
auto f = system_of_f.spawn(typed_second_stage);
CAF_CHECK(system_of_g.registry().running() == 1);
auto h = f * g;
CAF_CHECK(system_of_g.registry().running() == 1);
......@@ -99,8 +99,8 @@ CAF_TEST(identity) {
// spawned dead if `g` is already dead upon spawning
CAF_TEST(lifetime_1a) {
auto g = system.spawn(dbl_bhvr);
auto f = system.spawn(dbl_bhvr);
auto g = system.spawn(testee);
auto f = system.spawn(testee);
self->monitor(g);
anon_send_exit(g, exit_reason::kill);
wait_until_exited();
......@@ -111,8 +111,8 @@ CAF_TEST(lifetime_1a) {
// spawned dead if `f` is already dead upon spawning
CAF_TEST(lifetime_1b) {
auto g = system.spawn(dbl_bhvr);
auto f = system.spawn(dbl_bhvr);
auto g = system.spawn(testee);
auto f = system.spawn(testee);
self->monitor(f);
anon_send_exit(f, exit_reason::kill);
wait_until_exited();
......@@ -123,8 +123,8 @@ CAF_TEST(lifetime_1b) {
// `f.g` exits when `g` exits
CAF_TEST(lifetime_2a) {
auto g = system.spawn(dbl_bhvr);
auto f = system.spawn(dbl_bhvr);
auto g = system.spawn(testee);
auto f = system.spawn(testee);
auto h = f * g;
self->monitor(h);
anon_send(g, message{});
......@@ -134,8 +134,8 @@ CAF_TEST(lifetime_2a) {
// `f.g` exits when `f` exits
CAF_TEST(lifetime_2b) {
auto g = system.spawn(dbl_bhvr);
auto f = system.spawn(dbl_bhvr);
auto g = system.spawn(testee);
auto f = system.spawn(testee);
auto h = f * g;
self->monitor(h);
anon_send(f, message{});
......@@ -147,13 +147,13 @@ CAF_TEST(lifetime_2b) {
// 2) exits by receiving an exit message
// 3) exit has no effect on constituent actors
CAF_TEST(lifetime_3) {
auto g = system.spawn(dbl_bhvr);
auto f = system.spawn(dbl_bhvr);
auto g = system.spawn(testee);
auto f = system.spawn(testee);
auto h = f * g;
self->monitor(h);
anon_send(h, down_msg{self->address(), exit_reason::kill});
CAF_CHECK(! exited(h));
auto em_sender = system.spawn(dbl_bhvr);
auto em_sender = system.spawn(testee);
em_sender->link_to(h.address());
anon_send_exit(em_sender, exit_reason::kill);
wait_until_exited();
......@@ -178,8 +178,8 @@ CAF_TEST(lifetime_3) {
}
CAF_TEST(request_response_promise) {
auto g = system.spawn(dbl_bhvr);
auto f = system.spawn(dbl_bhvr);
auto g = system.spawn(testee);
auto f = system.spawn(testee);
auto h = f * g;
anon_send_exit(h, exit_reason::kill);
CAF_CHECK(exited(h));
......@@ -197,8 +197,8 @@ CAF_TEST(request_response_promise) {
// single composition of distinct actors
CAF_TEST(dot_composition_1) {
auto first = system.spawn(first_stage_impl);
auto second = system.spawn(second_stage_impl);
auto first = system.spawn(typed_first_stage);
auto second = system.spawn(typed_second_stage);
auto first_then_second = second * first;
self->request(first_then_second, 42).receive(
[](double res) {
......@@ -211,7 +211,7 @@ CAF_TEST(dot_composition_1) {
// multiple self composition
CAF_TEST(dot_composition_2) {
auto dbl_actor = system.spawn(dbl_bhvr);
auto dbl_actor = system.spawn(testee);
auto dbl_x4_actor = dbl_actor * dbl_actor
* dbl_actor * dbl_actor;
self->request(dbl_x4_actor, 1).receive(
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE splitter
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
using namespace caf;
namespace {
using first_stage = typed_actor<replies_to<double>::with<double, double>>;
using second_stage = typed_actor<replies_to<double, double>::with<double>>;
first_stage::behavior_type typed_first_stage() {
return [](double x) {
return std::make_tuple(x * 2.0, x * 4.0);
};
};
second_stage::behavior_type typed_second_stage() {
return [](double x, double y) {
return x * y;
};
}
behavior untyped_first_stage() {
return typed_first_stage().unbox();
};
behavior untyped_second_stage() {
return typed_second_stage().unbox();
}
struct fixture {
actor_system system;
scoped_actor self{system, true};
actor first;
actor second;
actor first_and_second;
~fixture() {
anon_send_exit(first, exit_reason::kill);
anon_send_exit(second, exit_reason::kill);
}
void init_untyped() {
using namespace std::placeholders;
first = system.spawn(untyped_first_stage);
second = system.spawn(untyped_second_stage);
first_and_second = splice(first, second.bind(23.0, _1));
}
void await_down(const actor& x) {
self->monitor(x);
self->receive(
[&](const down_msg& dm) -> maybe<skip_message_t> {
if (dm.source != x)
return skip_message();
return none;
}
);
}
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(sequencer_tests, fixture)
CAF_TEST(identity) {
init_untyped();
CAF_CHECK(first != second);
CAF_CHECK(first != first_and_second);
CAF_CHECK(second != first_and_second);
}
CAF_TEST(kill_first) {
init_untyped();
anon_send_exit(first, exit_reason::kill);
await_down(first_and_second);
}
CAF_TEST(kill_second) {
init_untyped();
anon_send_exit(second, exit_reason::kill);
await_down(first_and_second);
}
CAF_TEST(untyped_splicing) {
init_untyped();
self->request(first_and_second, 42.0).receive(
[](double x, double y, double z) {
CAF_CHECK(x == (42.0 * 2.0));
CAF_CHECK(y == (42.0 * 4.0));
CAF_CHECK(z == (23.0 * 42.0));
}
);
}
CAF_TEST(typed_splicing) {
using namespace std::placeholders;
auto x = system.spawn(typed_first_stage);
auto y = system.spawn(typed_second_stage);
auto x_and_y = splice(x, y.bind(23.0, _1));
using expected_type = typed_actor<replies_to<double>
::with<double, double, double>>;
static_assert(std::is_same<decltype(x_and_y), expected_type>::value,
"splice() did not compute the correct result");
self->request(x_and_y, 42.0).receive(
[](double x, double y, double z) {
CAF_CHECK(x == (42.0 * 2.0));
CAF_CHECK(y == (42.0 * 4.0));
CAF_CHECK(z == (23.0 * 42.0));
}
);
anon_send_exit(x, exit_reason::kill);
anon_send_exit(y, exit_reason::kill);
}
CAF_TEST_FIXTURE_SCOPE_END()
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