Commit 2e2eb1c5 authored by Dominik Charousset's avatar Dominik Charousset

Add error, exit, and down handlers

parent 23e33b4f
......@@ -106,6 +106,13 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
assert(self->num_connections() == 1);
// monitor buddy to quit broker if buddy is done
self->monitor(buddy);
self->set_down_handler([=](down_msg& dm) {
if (dm.source == buddy) {
aout(self) << "our buddy is down" << endl;
// quit for same reason
self->quit(dm.reason);
}
});
// setup: we are exchanging only messages consisting of an atom
// (as uint64_t) and an integer value (int32_t)
self->configure_read(hdl, receive_policy::exactly(sizeof(uint64_t) +
......@@ -130,13 +137,6 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
write_int(self, hdl, static_cast<uint64_t>(av));
write_int(self, hdl, i);
},
[=](const down_msg& dm) {
if (dm.source == buddy) {
aout(self) << "our buddy is down" << endl;
// quit for same reason
self->quit(dm.reason);
}
},
[=](const new_data_msg& msg) {
// read the atom value as uint64_t from buffer
uint64_t atm_val;
......
......@@ -46,6 +46,9 @@ behavior connection_worker(broker* self, connection_handle hdl) {
behavior server(broker* self) {
auto counter = std::make_shared<int>(0);
self->set_down_handler([=](down_msg&) {
++*counter;
});
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value);
return {
[=](const new_connection_msg& ncm) {
......@@ -53,9 +56,6 @@ behavior server(broker* self) {
self->monitor(worker);
self->link_to(worker);
},
[=](const down_msg&) {
++*counter;
},
[=](tick_atom) {
aout(self) << "Finished " << *counter << " requests per second." << endl;
*counter = 0;
......
......@@ -5,7 +5,7 @@
// This example is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 17-21, 31-65, 67-101, and 135-140 (Actor.tex)
// Manual references: lines 17-21, 31-65, 67-101, and 134-139 (Actor.tex)
#include <iostream>
......@@ -107,7 +107,6 @@ void tester(scoped_actor&) {
// tests a calculator instance
template <class Handle, class... Ts>
void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) {
self->monitor(hdl);
// first test: x + y = z
self->request(hdl, infinite, add_atom::value, x, y).receive(
[&](int res1) {
......@@ -116,7 +115,6 @@ void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) {
self->request(hdl, infinite, sub_atom::value, x, y).receive(
[&](int res2) {
aout(self) << x << " - " << y << " = " << res2 << endl;
self->send_exit(hdl, exit_reason::user_shutdown);
}
);
},
......@@ -126,7 +124,6 @@ void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) {
self->quit(exit_reason::user_shutdown);
}
);
self->receive([](const down_msg&) {});
tester(self, std::forward<Ts>(xs)...);
}
......
......@@ -64,11 +64,11 @@ int main() {
cells.emplace_back(system.spawn(cell_impl, i * i));
scoped_actor self{system};
aout(self) << "waiting_testee" << endl;
self->spawn<monitored>(waiting_testee, cells);
self->receive([](const down_msg&) {});
auto x1 = self->spawn(waiting_testee, cells);
self->wait_for(x1);
aout(self) << "multiplexed_testee" << endl;
self->spawn<monitored>(multiplexed_testee, cells);
self->receive([](const down_msg&) {});
auto x2 = self->spawn(multiplexed_testee, cells);
self->wait_for(x2);
aout(self) << "blocking_testee" << endl;
system.spawn(blocking_testee, cells);
}
......@@ -21,7 +21,6 @@
#define CAF_ABSTRACT_ACTOR_HPP
#include <set>
#include <mutex>
#include <atomic>
#include <memory>
#include <string>
......@@ -29,7 +28,6 @@
#include <cstdint>
#include <exception>
#include <type_traits>
#include <condition_variable>
#include "caf/fwd.hpp"
#include "caf/node_id.hpp"
......@@ -134,8 +132,6 @@ public:
};
// flags storing runtime information used by ...
static constexpr int trap_exit_flag = 0x0001; // local_actor
static constexpr int trap_error_flag = 0x0002; // local_actor
static constexpr int has_timeout_flag = 0x0004; // single_timeout
static constexpr int is_registered_flag = 0x0008; // (several actors)
static constexpr int is_initialized_flag = 0x0010; // event-based actors
......
......@@ -23,8 +23,8 @@
#include <mutex>
#include <condition_variable>
#include "caf/send.hpp"
#include "caf/none.hpp"
#include "caf/extend.hpp"
#include "caf/behavior.hpp"
#include "caf/local_actor.hpp"
......@@ -57,6 +57,8 @@ public:
~blocking_actor();
void enqueue(mailbox_element_ptr, execution_unit*) override;
/**************************************************************************
* utility stuff and receive() member function family *
**************************************************************************/
......@@ -72,7 +74,8 @@ public:
static_assert(sizeof...(Ts) > 0,
"operator() requires at least one argument");
behavior bhvr{std::forward<Ts>(xs)...};
while (stmt_()) dq_(bhvr);
while (stmt_())
dq_(bhvr);
}
};
......@@ -185,6 +188,26 @@ public:
/// Implements the actor's behavior.
virtual void act();
/// Blocks this actor until all `xs...` have terminated.
template <class... Ts>
void wait_for(Ts&&... xs) {
using wait_for_atom = atom_constant<atom("waitFor")>;
auto old = default_handler_;
default_handler_ = skip;
size_t expected = 0;
size_t i = 0;
size_t attach_results[] = {attach_functor(xs)...};
for (auto res : attach_results)
expected += res;
receive_for(i, expected)(
[](wait_for_atom) {
// nop
}
);
// restore custom default handler
default_handler_ = old;
}
/// @cond PRIVATE
inline const error& fail_state() {
......@@ -195,7 +218,7 @@ public:
void dequeue(behavior& bhvr, message_id mid = invalid_message_id);
using local_actor::await_data;
void await_data();
/// @endcond
......@@ -204,6 +227,26 @@ protected:
std::function<void(behavior&)> make_dequeue_callback() {
return [=](behavior& bhvr) { dequeue(bhvr); };
}
private:
size_t attach_functor(const actor&);
size_t attach_functor(const actor_addr&);
size_t attach_functor(const strong_actor_ptr&);
template <class... Ts>
size_t attach_functor(const typed_actor<Ts...>& x) {
return attach_functor(actor_cast<strong_actor_ptr>(x));
}
template <class Container>
size_t attach_functor(const Container& xs) {
size_t res = 0;
for (auto& x : xs)
res += attach_functor(x);
return res;
}
};
} // namespace caf
......
......@@ -64,6 +64,7 @@ using sorted_builtin_types =
strong_actor_ptr, // @strong_actor_ptr
std::set<std::string>, // @strset
std::vector<std::string>, // @strvec
sync_timeout_msg, // @sync_timeout_msg
timeout_msg, // @timeout
uint16_t, // @u16
std::u16string, // @u16_str
......
......@@ -61,17 +61,6 @@ struct deduce_rhs_result {
using type = type_list<>;
};
template <class T>
struct is_hidden_msg_handler : std::false_type { };
template <>
struct is_hidden_msg_handler<typed_mpi<type_list<exit_msg>,
type_list<void>>> : std::true_type { };
template <>
struct is_hidden_msg_handler<typed_mpi<type_list<down_msg>,
type_list<void>>> : std::true_type { };
template <class T>
struct deduce_mpi {
using result = typename implicit_conversions<typename T::result_type>::type;
......
......@@ -74,6 +74,7 @@ class proxy_registry;
class continue_helper;
class mailbox_element;
class message_handler;
class sync_timeout_msg;
class response_promise;
class event_based_actor;
class type_erased_tuple;
......
......@@ -114,9 +114,19 @@ public:
using mailbox_type = detail::single_reader_queue<mailbox_element,
detail::disposer>;
using unexpected_handler
= std::function<result<message>(local_actor* self,
const type_erased_tuple*)>;
/// Function object for handling unmatched messages.
using default_handler
= std::function<result<message> (local_actor* self,
const type_erased_tuple*)>;
/// Function object for handling error messages.
using error_handler = std::function<void (local_actor* self, error&)>;
/// Function object for handling down messages.
using down_handler = std::function<void (local_actor* self, down_msg&)>;
/// Function object for handling exit messages.
using exit_handler = std::function<void (local_actor* self, exit_msg&)>;
// -- constructors, destructors, and assignment operators --------------------
......@@ -246,12 +256,12 @@ public:
}
/// Sends an exit message to `dest`.
void send_exit(const actor_addr& dest, exit_reason reason);
void send_exit(const actor_addr& dest, error reason);
/// Sends an exit message to `dest`.
template <class ActorHandle>
void send_exit(const ActorHandle& dest, exit_reason reason) {
send_exit(dest.address(), reason);
void send_exit(const ActorHandle& dest, error reason) {
send_exit(dest.address(), std::move(reason));
}
/// Sends a message to `dest` that is delayed by `rel_time`
......@@ -311,8 +321,41 @@ public:
// -- miscellaneous actor operations -----------------------------------------
/// Sets a custom handler for unexpected messages.
inline void set_default_handler(unexpected_handler fun) {
unexpected_handler_ = std::move(fun);
inline void set_default_handler(default_handler fun) {
default_handler_ = std::move(fun);
}
/// Sets a custom handler for error messages.
inline void set_error_handler(error_handler fun) {
error_handler_ = std::move(fun);
}
/// Sets a custom handler for error messages.
template <class T>
auto set_error_handler(T fun) -> decltype(fun(std::declval<error&>())) {
set_error_handler([fun](local_actor*, error& x) { fun(x); });
}
/// Sets a custom handler for down messages.
inline void set_down_handler(down_handler fun) {
down_handler_ = std::move(fun);
}
/// Sets a custom handler for down messages.
template <class T>
auto set_down_handler(T fun) -> decltype(fun(std::declval<down_msg&>())) {
set_down_handler([fun](local_actor*, down_msg& x) { fun(x); });
}
/// Sets a custom handler for error messages.
inline void set_exit_handler(exit_handler fun) {
exit_handler_ = std::move(fun);
}
/// Sets a custom handler for exit messages.
template <class T>
auto set_exit_handler(T fun) -> decltype(fun(std::declval<exit_msg&>())) {
set_exit_handler([fun](local_actor*, exit_msg& x) { fun(x); });
}
/// Returns the execution unit currently used by this actor.
......@@ -357,26 +400,6 @@ public:
/// blocking API calls such as {@link receive()}.
void quit(error reason = error{});
/// Checks whether this actor traps exit messages.
inline bool trap_exit() const {
return get_flag(trap_exit_flag);
}
/// Enables or disables trapping of exit messages.
inline void trap_exit(bool value) {
set_flag(value, trap_exit_flag);
}
/// Checks whether this actor traps error messages.
inline bool trap_error() const {
return get_flag(trap_error_flag);
}
/// Enables or disables trapping of error messages.
inline void trap_error(bool value) {
set_flag(value, trap_error_flag);
}
/// @cond PRIVATE
void monitor(abstract_actor* whom);
......@@ -635,8 +658,6 @@ public:
behavior& fun,
message_id awaited_response);
using error_handler = std::function<void (error&)>;
using pending_response = std::pair<const message_id, behavior>;
message_id new_request_id(message_priority mp);
......@@ -684,9 +705,6 @@ public:
void do_become(behavior bhvr, bool discard_old);
protected:
// used only in thread-mapped actors
void await_data();
// used by both event-based and blocking actors
mailbox_type mailbox_;
......@@ -719,7 +737,16 @@ protected:
std::set<group> subscriptions_;
// used for setting custom functions for handling unexpected messages
unexpected_handler unexpected_handler_;
default_handler default_handler_;
// used for setting custom error handlers
error_handler error_handler_;
// used for setting custom down message handlers
down_handler down_handler_;
// used for setting custom exit message handlers
exit_handler exit_handler_;
/// @endcond
......@@ -741,6 +768,22 @@ private:
void delayed_send_impl(message_id mid, strong_actor_ptr whom,
const duration& rtime, message data);
enum class msg_type {
expired_timeout, // an 'old & obsolete' timeout
timeout, // triggers currently active timeout
ordinary, // an asynchronous message or sync. request
response, // a response
sys_message // a system message, e.g., exit_msg or down_msg
};
msg_type filter_msg(mailbox_element& node);
void handle_response(mailbox_element_ptr&, local_actor::pending_response&);
class private_thread;
private_thread* private_thread_;
};
/// A smart pointer to a {@link local_actor} instance.
......
......@@ -158,6 +158,14 @@ public:
param_decay
>::type;
static_assert(! std::is_same<pattern, detail::type_list<exit_msg>>::value,
"exit_msg not allowed in message handlers, "
"did you mean to use set_exit_handler()?");
static_assert(! std::is_same<pattern, detail::type_list<down_msg>>::value,
"down_msg not allowed in message handlers, "
"did you mean to use set_down_handler()?");
using intermediate_tuple =
typename detail::tl_apply<
typename detail::tl_map<
......
......@@ -34,9 +34,8 @@
namespace caf {
/// Sent to all links when an actor is terminated.
/// @note This message can be handled manually by calling
/// `local_actor::trap_exit(true)` and is otherwise handled
/// implicitly by the runtime system.
/// @note Actors can override the default handler by calling
/// `self->set_exit_handler(...)`.
struct exit_msg {
/// The source of this message, i.e., the terminated actor.
actor_addr source;
......@@ -48,6 +47,12 @@ inline std::string to_string(const exit_msg& x) {
return "exit" + deep_to_string(std::tie(x.source, x.reason));
}
template <class Processor>
void serialize(Processor& proc, exit_msg& x, const unsigned int) {
proc & x.source;
proc & x.reason;
}
/// Sent to all actors monitoring an actor when it is terminated.
struct down_msg {
/// The source of this message, i.e., the terminated actor.
......@@ -60,41 +65,8 @@ inline std::string to_string(const down_msg& x) {
return "down" + deep_to_string(std::tie(x.source, x.reason));
}
template <class T>
typename std::enable_if<
detail::tl_exists<
detail::type_list<exit_msg, down_msg>,
detail::tbind<std::is_same, T>::template type
>::value,
bool
>::type
operator==(const T& lhs, const T& rhs) {
return lhs.source == rhs.source && lhs.reason == rhs.reason;
}
template <class T>
typename std::enable_if<
detail::tl_exists<
detail::type_list<exit_msg, down_msg>,
detail::tbind<std::is_same, T>::template type
>::value,
bool
>::type
operator!=(const T& lhs, const T& rhs) {
return !(lhs == rhs);
}
template <class Processor, class T>
typename std::enable_if<
detail::tl_exists<
detail::type_list<exit_msg, down_msg>,
detail::tbind<
std::is_same,
typename std::remove_const<T>::type
>::template type
>::value
>::type
serialize(Processor& proc, T& x, const unsigned int) {
template <class Processor>
void serialize(Processor& proc, down_msg& x, const unsigned int) {
proc & x.source;
proc & x.reason;
}
......@@ -109,16 +81,6 @@ inline std::string to_string(const group_down_msg& x) {
return "group_down" + deep_to_string(std::tie(x.source));
}
/// @relates group_down_msg
inline bool operator==(const group_down_msg& lhs, const group_down_msg& rhs) {
return lhs.source == rhs.source;
}
/// @relates group_down_msg
inline bool operator!=(const group_down_msg& lhs, const group_down_msg& rhs) {
return !(lhs == rhs);
}
/// @relates group_down_msg
template <class Processor>
void serialize(Processor& proc, group_down_msg& x, const unsigned int) {
......@@ -128,20 +90,16 @@ void serialize(Processor& proc, group_down_msg& x, const unsigned int) {
/// Sent whenever a timeout occurs during a synchronous send.
/// This system message does not have any fields, because the message ID
/// sent alongside this message identifies the matching request that timed out.
struct sync_timeout_msg { };
class sync_timeout_msg { };
inline std::string to_string(const sync_timeout_msg&) {
return "sync_timeout";
}
/// @relates sync_timeout_msg
inline bool operator==(const sync_timeout_msg&, const sync_timeout_msg&) {
return true;
}
/// @relates sync_timeout_msg
inline bool operator!=(const sync_timeout_msg&, const sync_timeout_msg&) {
return false;
/// @relates group_down_msg
template <class Processor>
void serialize(Processor&, sync_timeout_msg&, const unsigned int) {
// nop
}
/// Signalizes a timeout event.
......@@ -155,16 +113,6 @@ inline std::string to_string(const timeout_msg& x) {
return "timeout" + deep_to_string(std::tie(x.timeout_id));
}
/// @relates timeout_msg
inline bool operator==(const timeout_msg& lhs, const timeout_msg& rhs) {
return lhs.timeout_id == rhs.timeout_id;
}
/// @relates timeout_msg
inline bool operator!=(const timeout_msg& lhs, const timeout_msg& rhs) {
return !(lhs == rhs);
}
/// @relates timeout_msg
template <class Processor>
void serialize(Processor& proc, timeout_msg& x, const unsigned int) {
......
......@@ -209,11 +209,7 @@ private:
template <class... Ts>
void set(intrusive_ptr<detail::default_behavior_impl<std::tuple<Ts...>>> bp) {
using mpi =
typename detail::tl_filter_not<
detail::type_list<typename detail::deduce_mpi<Ts>::type...>,
detail::is_hidden_msg_handler
>::type;
using mpi = detail::type_list<typename detail::deduce_mpi<Ts>::type...>;
static_assert(detail::tl_is_distinct<mpi>::value,
"multiple handler defintions found");
detail::static_asserter<signatures, mpi, detail::ctm>::verify_match();
......
......@@ -78,30 +78,25 @@ public:
// nop
}
inline mailbox_element_ptr dequeue() {
blocking_actor::await_data();
return next_message();
}
bool await_data(const hrc::time_point& tp) {
if (has_next_message()) {
if (has_next_message())
return true;
}
return mailbox().synchronized_await(mtx_, cv_, tp);
}
mailbox_element_ptr try_dequeue() {
blocking_actor::await_data();
return next_message();
}
mailbox_element_ptr try_dequeue(const hrc::time_point& tp) {
if (await_data(tp)) {
if (await_data(tp))
return next_message();
}
return mailbox_element_ptr{};
}
void act() override {
trap_exit(true);
// setup & local variables
bool received_exit = false;
mailbox_element_ptr msg_ptr;
std::multimap<hrc::time_point, delayed_msg> messages;
// message handling rules
message_handler mfun{
......@@ -109,17 +104,14 @@ public:
message_id mid, message& msg) {
insert_dmsg(messages, d, std::move(from),
std::move(to), mid, std::move(msg));
},
[&](const exit_msg&) {
received_exit = true;
}
};
// loop
while (! received_exit) {
mailbox_element_ptr msg_ptr;
for (;;) {
while (! msg_ptr) {
if (messages.empty())
msg_ptr = dequeue();
else {
if (messages.empty()) {
msg_ptr = try_dequeue();
} else {
auto tout = hrc::now();
// handle timeouts (send messages)
auto it = messages.begin();
......@@ -128,11 +120,15 @@ public:
it = messages.erase(it);
}
// wait for next message or next timeout
if (it != messages.end()) {
if (it != messages.end())
msg_ptr = try_dequeue(it->first);
}
}
}
if (msg_ptr->msg.match_element<exit_msg>(0)) {
auto& em = msg_ptr->msg.get_as<exit_msg>(0);
if (em.reason)
quit(em.reason);
}
mfun(msg_ptr->msg);
msg_ptr.reset();
}
......@@ -268,9 +264,14 @@ void printer_loop(blocking_actor* self) {
std::cout << line << std::flush;
line.clear();
};
self->trap_exit(true);
/*
bool running = true;
self->set_exit_handler([&](exit_msg&) {
running = false;
});
self->receive_while([&] { return running; })(
*/
self->receive_loop(
[&](add_atom, actor_id aid, std::string& str) {
if (str.empty() || aid == invalid_actor_id)
return;
......@@ -297,9 +298,6 @@ void printer_loop(blocking_actor* self) {
auto d = get_data(aid, true);
if (d)
d->redirect = get_sink_handle(self->system(), fcache, fn, flag);
},
[&](const exit_msg&) {
running = false;
}
);
}
......@@ -333,16 +331,9 @@ void* abstract_coordinator::subtype_ptr() {
void abstract_coordinator::stop_actors() {
CAF_LOG_TRACE("");
scoped_actor self{system_, true};
self->monitor(timer_);
self->monitor(printer_);
anon_send_exit(timer_, exit_reason::user_shutdown);
anon_send_exit(printer_, exit_reason::user_shutdown);
int i = 0;
self->receive_for(i, 2)(
[](const down_msg&) {
// nop
}
);
self->wait_for(timer_, printer_);
}
abstract_coordinator::abstract_coordinator(actor_system& sys)
......
......@@ -182,6 +182,10 @@ void actor_registry::start() {
self->state.data[key].second.erase(subscriber);
subscribers.erase(i);
};
self->set_down_handler([=](down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
unsubscribe_all(actor_cast<actor>(dm.source));
});
return {
[=](put_atom, const std::string& key, message& msg) {
CAF_LOG_TRACE(CAF_ARG(key) << CAF_ARG(msg));
......@@ -237,10 +241,6 @@ void actor_registry::start() {
}
self->state.subscribers[subscriber].erase(key);
self->state.data[key].second.erase(subscriber);
},
[=](const down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
unsubscribe_all(actor_cast<actor>(dm.source));
}
};
};
......
......@@ -17,13 +17,15 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/exception.hpp"
#include "caf/actor_system.hpp"
#include "caf/blocking_actor.hpp"
#include "caf/logger.hpp"
#include "caf/exception.hpp"
#include "caf/actor_system.hpp"
#include "caf/actor_registry.hpp"
#include "caf/detail/sync_request_bouncer.hpp"
namespace caf {
blocking_actor::blocking_actor(actor_config& sys)
......@@ -35,6 +37,18 @@ blocking_actor::~blocking_actor() {
// avoid weak-vtables warning
}
void blocking_actor::enqueue(mailbox_element_ptr ptr, execution_unit*) {
auto mid = ptr->mid;
auto sender = ptr->sender;
// returns false if mailbox has been closed
if (! mailbox().synchronized_enqueue(mtx_, cv_, ptr.release())) {
if (mid.is_request()) {
detail::sync_request_bouncer srb{exit_reason()};
srb(sender, mid);
}
}
}
void blocking_actor::await_all_other_actors_done() {
system().registry().await_running_count_equal(is_registered() ? 1 : 0);
}
......@@ -51,16 +65,16 @@ void blocking_actor::initialize() {
void blocking_actor::dequeue(behavior& bhvr, message_id mid) {
CAF_LOG_TRACE(CAF_ARG(mid));
// push an empty sync response handler for `blocking_actor`
if (mid != invalid_message_id && ! find_awaited_response(mid))
awaited_responses_.emplace_front(mid, behavior{});
// try to dequeue from cache first
if (invoke_from_cache(bhvr, mid))
return;
// requesting an invalid timeout will reset our active timeout
uint32_t timeout_id = 0;
if (mid == invalid_message_id)
if (mid != invalid_message_id)
awaited_responses_.emplace_front(mid, bhvr);
else
timeout_id = request_timeout(bhvr.timeout());
if (mid != invalid_message_id && ! find_awaited_response(mid))
awaited_responses_.emplace_front(mid, behavior{});
// read incoming messages
for (;;) {
await_data();
......@@ -81,4 +95,28 @@ void blocking_actor::dequeue(behavior& bhvr, message_id mid) {
}
}
void blocking_actor::await_data() {
if (! has_next_message())
mailbox().synchronized_await(mtx_, cv_);
}
size_t blocking_actor::attach_functor(const actor& x) {
return attach_functor(actor_cast<strong_actor_ptr>(x));
}
size_t blocking_actor::attach_functor(const actor_addr& x) {
return attach_functor(actor_cast<strong_actor_ptr>(x));
}
size_t blocking_actor::attach_functor(const strong_actor_ptr& ptr) {
using wait_for_atom = atom_constant<atom("waitFor")>;
if (! ptr)
return 0;
auto self = actor_cast<actor>(this);
ptr->get()->attach_functor([=](const error&) {
anon_send(self, wait_for_atom::value);
});
return 1;
}
} // namespace caf
......@@ -48,23 +48,14 @@ class local_group_module;
void await_all_locals_down(actor_system& sys, std::initializer_list<actor> xs) {
CAF_LOG_TRACE("");
size_t awaited_down_msgs = 0;
scoped_actor self{sys, true};
std::vector<actor> ys;
for (auto& x : xs)
if (x != invalid_actor && x.node() == sys.node()) {
self->monitor(x);
if (x.node() == sys.node()) {
self->send_exit(x, exit_reason::kill);
++awaited_down_msgs;
ys.push_back(x);
}
size_t i = 0;
self->receive_for(i, awaited_down_msgs) (
[](const down_msg&) {
// nop
},
after(std::chrono::seconds(1)) >> [&] {
throw std::logic_error("at least one actor did not quit within 1s");
}
);
self->wait_for(ys);
}
class local_group : public abstract_group {
......@@ -170,6 +161,18 @@ public:
return message{};
};
set_default_handler(fwd);
set_down_handler([=](down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
if (dm.source) {
auto first = acquaintances_.begin();
auto last = acquaintances_.end();
auto i = std::find_if(first, last, [&](const actor& a) {
return a == dm.source;
});
if (i != last)
acquaintances_.erase(i);
}
});
// return behavior
return {
[=](join_atom, const actor& other) {
......@@ -190,18 +193,6 @@ public:
group_->send_all_subscribers(current_element_->sender, what, context());
// forward to all acquaintances
send_to_acquaintances(what);
},
[=](const down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
if (dm.source) {
auto first = acquaintances_.begin();
auto last = acquaintances_.end();
auto i = std::find_if(first, last, [&](const actor& a) {
return a == dm.source;
});
if (i != last)
acquaintances_.erase(i);
}
}
};
}
......@@ -305,13 +296,16 @@ private:
local_group_proxy* grp) {
CAF_LOG_TRACE("");
self->monitor(grp->broker_);
self->set_down_handler([=](down_msg& down) {
CAF_LOG_TRACE(CAF_ARG(down));
auto msg = make_message(group_down_msg{group(grp)});
grp->send_all_subscribers(self->ctrl(), std::move(msg),
self->context());
self->quit(down.reason);
});
return {
[=](const down_msg& down) {
CAF_LOG_TRACE(CAF_ARG(down));
auto msg = make_message(group_down_msg{group(grp)});
grp->send_all_subscribers(self->ctrl(), std::move(msg),
self->context());
self->quit(down.reason);
[] {
// nop
}
};
}
......@@ -332,7 +326,7 @@ behavior proxy_broker::make_behavior() {
set_default_handler(fwd);
// return dummy behavior
return {
[](const down_msg&) {
[] {
// nop
}
};
......
This diff is collapsed.
......@@ -78,6 +78,7 @@ const char* numbered_type_names[] = {
"@strong_actor_ptr",
"@strset",
"@strvec",
"@sync_timeout_msg",
"@timeout",
"@u16",
"@u16str",
......
......@@ -32,6 +32,8 @@ using std::endl;
namespace {
using down_atom = atom_constant<atom("down")>;
struct fixture {
actor_system_config cfg;
......@@ -52,12 +54,8 @@ struct fixture {
CAF_CHECK(ifs.empty());
auto aut = actor_cast<actor>(res);
CAF_REQUIRE(aut != invalid_actor);
self->monitor(aut);
self->receive(
[](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
}
);
self->wait_for(aut);
CAF_MESSAGE("aut done");
}
};
......
......@@ -66,14 +66,20 @@ public:
template <class ExitMsgType>
behavior tester(event_based_actor* self, const actor& aut) {
if (std::is_same<ExitMsgType, exit_msg>::value) {
self->trap_exit(true);
self->set_exit_handler([self](exit_msg& msg) {
// must be still alive at this point
CAF_CHECK_EQUAL(s_testees.load(), 1);
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
// testee might be still running its cleanup code in
// another worker thread; by waiting some milliseconds, we make sure
// testee had enough time to return control to the scheduler
// which in turn destroys it by dropping the last remaining reference
self->delayed_send(self, std::chrono::milliseconds(30),
check_atom::value);
});
self->link_to(aut);
} else {
self->monitor(aut);
}
anon_send_exit(aut, exit_reason::user_shutdown);
return {
[self](const ExitMsgType& msg) {
self->set_down_handler([self](down_msg& msg) {
// must be still alive at this point
CAF_CHECK_EQUAL(s_testees.load(), 1);
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
......@@ -83,7 +89,11 @@ behavior tester(event_based_actor* self, const actor& aut) {
// which in turn destroys it by dropping the last remaining reference
self->delayed_send(self, std::chrono::milliseconds(30),
check_atom::value);
},
});
self->monitor(aut);
}
anon_send_exit(aut, exit_reason::user_shutdown);
return {
[self](check_atom) {
// make sure aut's dtor and on_exit() have been called
CAF_CHECK_EQUAL(s_testees.load(), 0);
......
......@@ -108,50 +108,29 @@ CAF_TEST(round_robin_actor_pool) {
}
);
anon_send_exit(workers.back(), exit_reason::user_shutdown);
self->receive(
[&](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.source, workers.back().address());
workers.pop_back();
// poll actor pool up to 10 times or until it removes the failed worker
bool success = false;
size_t i = 0;
while (! success && ++i <= 10) {
self->request(w, infinite, sys_atom::value, get_atom::value).receive(
[&](std::vector<actor>& ws) {
success = workers.size() == ws.size();
if (success) {
std::sort(ws.begin(), ws.end());
CAF_CHECK_EQUAL(workers, ws);
} else {
// wait a bit until polling again
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
);
}
CAF_REQUIRE(success);
},
after(std::chrono::milliseconds(250)) >> [] {
CAF_ERROR("didn't receive a down message");
}
);
CAF_MESSAGE("about to send exit to workers");
self->send_exit(w, exit_reason::user_shutdown);
for (int i = 0; i < 6; ++i) {
self->receive(
[&](const down_msg& dm) {
auto last = workers.end();
auto src = dm.source;
CAF_CHECK_NOT_EQUAL(src, invalid_actor_addr);
auto pos = std::find(workers.begin(), last, src);
if (pos != last)
workers.erase(pos);
},
after(std::chrono::milliseconds(250)) >> [] {
CAF_ERROR("didn't receive a down message");
self->wait_for(workers.back());
workers.pop_back();
// poll actor pool up to 10 times or until it removes the failed worker
bool success = false;
size_t i = 0;
while (! success && ++i <= 10) {
self->request(w, infinite, sys_atom::value, get_atom::value).receive(
[&](std::vector<actor>& ws) {
success = workers.size() == ws.size();
if (success) {
std::sort(ws.begin(), ws.end());
CAF_CHECK_EQUAL(workers, ws);
} else {
// wait a bit until polling again
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
);
}
CAF_REQUIRE(success);
CAF_MESSAGE("about to send exit to workers");
self->send_exit(w, exit_reason::user_shutdown);
self->wait_for(workers);
}
CAF_TEST(broadcast_actor_pool) {
......
......@@ -43,7 +43,9 @@ struct fixture {
actor testee;
fixture() : self(system), mirror(system.spawn(mirror_impl)) {
// nop
self->set_down_handler([](local_actor*, down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
});
}
template <class... Ts>
......@@ -52,11 +54,7 @@ struct fixture {
}
~fixture() {
self->receive(
[](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
}
);
self->wait_for(testee);
}
};
......
......@@ -40,14 +40,6 @@ behavior testee(event_based_actor* self) {
}
struct fixture {
void wait_until_is_terminated() {
self->receive(
[](const down_msg&) {
// nop
}
);
}
template <class Actor>
static bool exited(const Actor& handle) {
auto ptr = actor_cast<abstract_actor*>(handle);
......@@ -85,7 +77,7 @@ CAF_TEST(lifetime_1) {
auto dbl = system.spawn(testee);
self->monitor(dbl);
anon_send_exit(dbl, exit_reason::kill);
wait_until_is_terminated();
self->wait_for(dbl);
auto bound = dbl.bind(1);
CAF_CHECK(exited(bound));
}
......@@ -96,7 +88,7 @@ CAF_TEST(lifetime_2) {
auto bound = dbl.bind(1);
self->monitor(bound);
anon_send(dbl, message{});
wait_until_is_terminated();
self->wait_for(dbl);
}
CAF_TEST(request_response_promise) {
......
......@@ -134,6 +134,10 @@ bool operator==(const counting_string& x, const char* y) {
return x.str() == y;
}
std::string to_string(const counting_string& ref) {
return ref.str();
}
} // namespace <anonymous>
namespace std {
......
......@@ -53,19 +53,17 @@ CAF_TEST(constructor_attach) {
class spawner : public event_based_actor {
public:
spawner(actor_config& cfg) : event_based_actor(cfg), downs_(0) {
// nop
set_down_handler([=](down_msg& msg) {
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
CAF_CHECK_EQUAL(msg.source, testee_.address());
if (++downs_ == 2)
quit(msg.reason);
});
}
behavior make_behavior() {
testee_ = spawn<testee, monitored>(this);
return {
[=](const down_msg& msg) {
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
CAF_CHECK_EQUAL(msg.source, testee_.address());
if (++downs_ == 2) {
quit(msg.reason);
}
},
[=](done_atom, const error& reason) {
CAF_CHECK_EQUAL(reason, exit_reason::user_shutdown);
if (++downs_ == 2) {
......
......@@ -73,21 +73,19 @@ CAF_TEST(test_custom_exception_handler) {
auto testee3 = self->spawn<exception_testee, monitored>();
self->send(testee3, "foo");
// receive all down messages
auto i = 0;
self->receive_for(i, 3)(
[&](const down_msg& dm) {
if (dm.source == testee1) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
}
else if (dm.source == testee2) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unhandled_exception);
}
else if (dm.source == testee3) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::remote_link_unreachable);
}
else {
throw std::runtime_error("received message from unexpected source");
}
self->set_down_handler([&](down_msg& dm) {
if (dm.source == testee1) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
}
);
else if (dm.source == testee2) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unhandled_exception);
}
else if (dm.source == testee3) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::remote_link_unreachable);
}
else {
throw std::runtime_error("received message from unexpected source");
}
});
self->wait_for(testee1, testee2, testee3);
}
......@@ -278,11 +278,13 @@ behavior master(event_based_actor* self) {
behavior slave(event_based_actor* self, actor master) {
self->link_to(master);
self->trap_exit(true);
self->set_exit_handler([=](exit_msg& msg) {
CAF_MESSAGE("slave: received exit message");
self->quit(msg.reason);
});
return {
[=](const exit_msg& msg) {
CAF_MESSAGE("slave: received exit message");
self->quit(msg.reason);
[] {
// nop
}
};
}
......@@ -358,47 +360,29 @@ CAF_TEST(self_receive_with_zero_timeout) {
CAF_TEST(mirror) {
scoped_actor self{system};
auto mirror = self->spawn<simple_mirror, monitored>();
auto mirror = self->spawn<simple_mirror>();
self->send(mirror, "hello mirror");
self->receive (
[](const std::string& msg) {
CAF_CHECK_EQUAL(msg, "hello mirror");
}
);
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
[&](const down_msg& dm) {
if (dm.reason == exit_reason::user_shutdown)
CAF_MESSAGE("received `down_msg`");
else
CAF_ERROR("Unexpected message");
}
);
}
CAF_TEST(detached_mirror) {
scoped_actor self{system};
auto mirror = self->spawn<simple_mirror, monitored+detached>();
auto mirror = self->spawn<simple_mirror, detached>();
self->send(mirror, "hello mirror");
self->receive (
[](const std::string& msg) {
CAF_CHECK_EQUAL(msg, "hello mirror");
}
);
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
[&](const down_msg& dm) {
if (dm.reason == exit_reason::user_shutdown)
CAF_MESSAGE("received `down_msg`");
else
CAF_ERROR("Unexpected message");
}
);
}
CAF_TEST(priority_aware_mirror) {
scoped_actor self{system};
auto mirror = self->spawn<simple_mirror, monitored + priority_aware>();
auto mirror = self->spawn<simple_mirror, priority_aware>();
CAF_MESSAGE("spawned mirror");
self->send(mirror, "hello mirror");
self->receive (
......@@ -406,15 +390,6 @@ CAF_TEST(priority_aware_mirror) {
CAF_CHECK_EQUAL(msg, "hello mirror");
}
);
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
[&](const down_msg& dm) {
if (dm.reason == exit_reason::user_shutdown)
CAF_MESSAGE("received `down_msg`");
else
CAF_ERROR("Unexpected message");
}
);
}
CAF_TEST(send_to_self) {
......@@ -550,25 +525,22 @@ CAF_TEST(constructor_attach) {
class spawner : public event_based_actor {
public:
spawner(actor_config& cfg) : event_based_actor(cfg), downs_(0) {
// nop
set_down_handler([=](down_msg& msg) {
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
if (++downs_ == 2)
quit(msg.reason);
});
set_exit_handler([=](exit_msg& msg) {
send_exit(testee_, std::move(msg.reason));
});
}
behavior make_behavior() {
trap_exit(true);
testee_ = spawn<testee, monitored>(this);
return {
[=](const down_msg& msg) {
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
if (++downs_ == 2) {
quit(msg.reason);
}
},
[=](ok_atom, const error& reason) {
CAF_CHECK_EQUAL(reason, exit_reason::user_shutdown);
if (++downs_ == 2)
quit(reason);
},
[=](exit_msg& msg) {
delegate(testee_, std::move(msg));
}
};
}
......@@ -630,28 +602,36 @@ CAF_TEST(custom_exception_handler) {
auto testee3 = self->spawn<exception_testee, monitored>();
self->send(testee3, "foo");
// receive all down messages
auto i = 0;
self->receive_for(i, 3)(
[&](const down_msg& dm) {
if (dm.source == testee1) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unhandled_exception);
}
else if (dm.source == testee2) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unknown);
}
else if (dm.source == testee3) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unhandled_exception);
}
else {
throw std::runtime_error("received message from unexpected source");
}
int downs_received = 0;
self->set_down_handler([&](down_msg& dm) {
if (dm.source == testee1) {
++downs_received;
CAF_CHECK_EQUAL(dm.reason, exit_reason::unhandled_exception);
}
);
else if (dm.source == testee2) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unknown);
++downs_received;
}
else if (dm.source == testee3) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::unhandled_exception);
++downs_received;
}
else {
throw std::runtime_error("received message from unexpected source");
}
if (downs_received == 3)
self->send(self, message{});
});
// this exploits the fact that down messages are counted by dequeue
behavior dummy{[] {}};
self->dequeue(dummy);
}
CAF_TEST(kill_the_immortal) {
auto wannabe_immortal = system.spawn([](event_based_actor* self) -> behavior {
self->trap_exit(true);
self->set_exit_handler([](local_actor*, exit_msg&) {
// nop
});
return {
[] {
// nop
......@@ -659,14 +639,8 @@ CAF_TEST(kill_the_immortal) {
};
});
scoped_actor self{system};
self->monitor(wannabe_immortal);
self->send_exit(wannabe_immortal, exit_reason::kill);
self->receive(
[&](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::kill);
CAF_CHECK_EQUAL(dm.source, wannabe_immortal.address());
}
);
self->wait_for(wannabe_immortal);
}
CAF_TEST(move_only_argument) {
......
......@@ -58,7 +58,11 @@ public:
: event_based_actor(cfg),
aut_(std::move(aut)),
msg_(make_message(1, 2, 3)) {
// nop
set_down_handler([=](down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
CAF_CHECK_EQUAL(dm.source, aut_.address());
quit();
});
}
behavior make_behavior() override {
......@@ -69,11 +73,6 @@ public:
CAF_CHECK_EQUAL(a, 1);
CAF_CHECK_EQUAL(b, 2);
CAF_CHECK_EQUAL(c, 3);
},
[=](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
CAF_CHECK_EQUAL(dm.source, aut_.address());
quit();
}
};
}
......
......@@ -55,14 +55,6 @@ second_stage::behavior_type typed_second_stage() {
}
struct fixture {
void wait_until_is_terminated() {
self->receive(
[](const down_msg&) {
CAF_CHECK(true);
}
);
}
template <class Actor>
static bool exited(const Actor& handle) {
auto ptr = actor_cast<abstract_actor*>(handle);
......@@ -98,9 +90,8 @@ CAF_TEST(identity) {
CAF_TEST(lifetime_1a) {
auto g = system.spawn(testee);
auto f = system.spawn(testee);
self->monitor(g);
anon_send_exit(g, exit_reason::kill);
wait_until_is_terminated();
self->wait_for(g);
auto h = f * g;
CAF_CHECK(exited(h));
}
......@@ -109,9 +100,8 @@ CAF_TEST(lifetime_1a) {
CAF_TEST(lifetime_1b) {
auto g = system.spawn(testee);
auto f = system.spawn(testee);
self->monitor(f);
anon_send_exit(f, exit_reason::kill);
wait_until_is_terminated();
self->wait_for(f);
auto h = f * g;
CAF_CHECK(exited(h));
}
......
......@@ -70,17 +70,6 @@ struct fixture {
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) -> optional<skip_t> {
if (dm.source != x)
return skip();
return none;
}
);
}
};
} // namespace <anonymous>
......@@ -97,13 +86,13 @@ CAF_TEST(identity) {
CAF_TEST(kill_first) {
init_untyped();
anon_send_exit(first, exit_reason::kill);
await_down(first_and_second);
self->wait_for(first_and_second);
}
CAF_TEST(kill_second) {
init_untyped();
anon_send_exit(second, exit_reason::kill);
await_down(first_and_second);
self->wait_for(first_and_second);
}
CAF_TEST(untyped_splicing) {
......
......@@ -186,11 +186,10 @@ CAF_TEST(error_response_message) {
[](double) {
CAF_ERROR("unexpected ordinary response message received");
},
[](const error& err) {
[](error& err) {
CAF_CHECK_EQUAL(err.code(), static_cast<uint8_t>(sec::unexpected_message));
}
);
self->send(foo, get_atom::value, 3.14);
self->send(foo, get_atom::value, 42);
self->receive(
[](int x) {
......@@ -200,6 +199,12 @@ CAF_TEST(error_response_message) {
CAF_ERROR("unexpected ordinary response message received: " << x);
}
);
self->set_error_handler([&](error& err) {
CAF_CHECK_EQUAL(err.code(), static_cast<uint8_t>(sec::unexpected_message));
self->send(self, message{});
});
self->send(foo, get_atom::value, 3.14);
self->receive([] {});
}
// verify that delivering to a satisfied promise has no effect
......
......@@ -274,19 +274,15 @@ behavior foo(event_based_actor* self) {
}
int_actor::behavior_type int_fun2(int_actor::pointer self) {
self->trap_exit(true);
self->set_down_handler([=](down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
self->quit();
});
return {
[=](int i) {
self->monitor(self->current_sender());
return i * i;
},
[=](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
self->quit();
},
[=](const exit_msg&) {
CAF_ERROR("Unexpected message");
}
};
}
......@@ -333,17 +329,13 @@ struct fixture {
}
);
CAF_CHECK_EQUAL(system.registry().running(), 2u);
self->spawn<monitored>(client, self, ts);
auto c1 = self->spawn(client, self, ts);
self->receive(
[](passed_atom) {
CAF_MESSAGE("received `passed_atom`");
}
);
self->receive(
[](const down_msg& dm) {
CAF_CHECK(dm.reason == exit_reason::normal);
}
);
self->wait_for(c1);
CAF_CHECK_EQUAL(system.registry().running(), 2u);
}
};
......@@ -485,12 +477,8 @@ CAF_TEST(check_signature) {
}
};
};
self->spawn<monitored>(bar_action);
self->receive(
[](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
}
);
auto x = self->spawn(bar_action);
self->wait_for(x);
}
CAF_TEST_FIXTURE_SCOPE_END()
......
......@@ -264,6 +264,11 @@ void basp_broker_state::learned_new_node(const node_id& nid) {
auto& tmp = spawn_servers[nid];
tmp = system().spawn<hidden>([=](event_based_actor* this_actor) -> behavior {
CAF_LOG_TRACE("");
// terminate when receiving a down message
this_actor->set_down_handler([=](down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
this_actor->quit(std::move(dm.reason));
});
// skip messages until we receive the initial ok_atom
this_actor->set_default_handler(skip);
return {
......@@ -281,10 +286,6 @@ void basp_broker_state::learned_new_node(const node_id& nid) {
this_actor->delegate(config_serv, get_atom::value,
std::move(type), std::move(args));
return {};
},
[=](const down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
this_actor->quit(dm.reason);
}
);
},
......@@ -339,6 +340,10 @@ void basp_broker_state::learned_new_node_indirectly(const node_id& nid) {
auto connection_helper = [=](event_based_actor* helper, actor s) -> behavior {
CAF_LOG_TRACE(CAF_ARG(s));
helper->monitor(s);
helper->set_down_handler([=](down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
helper->quit(std::move(dm.reason));
});
return {
// this config is send from the remote `ConfigServ`
[=](ok_atom, const std::string&, message& msg) {
......@@ -368,10 +373,6 @@ void basp_broker_state::learned_new_node_indirectly(const node_id& nid) {
}
});
},
[=](const down_msg& dm) {
CAF_LOG_TRACE(CAF_ARG(dm));
helper->quit(dm.reason);
},
after(std::chrono::minutes(10)) >> [=] {
CAF_LOG_TRACE(CAF_ARG(""));
// nothing heard in about 10 minutes... just a call it a day, then
......
......@@ -310,13 +310,8 @@ void middleman::stop() {
hooks_.reset();
named_brokers_.clear();
scoped_actor self{system(), true};
self->monitor(manager_);
self->send_exit(manager_, exit_reason::user_shutdown);
self->receive(
[](const down_msg&) {
// nop
}
);
self->wait_for(manager_);
}
void middleman::init(actor_system_config& cfg) {
......
......@@ -46,7 +46,16 @@ public:
middleman_actor_impl(actor_config& cfg, actor default_broker)
: middleman_actor::base(cfg),
broker_(default_broker) {
// nop
set_down_handler([=](down_msg& dm) {
auto i = cached_.begin();
auto e = cached_.end();
while (i != e) {
if (get<1>(i->second) == dm.source)
i = cached_.erase(i);
else
++i;
}
});
}
void on_exit() override {
......@@ -158,16 +167,6 @@ public:
CAF_LOG_TRACE("");
delegate(broker_, atm, std::move(nid));
return {};
},
[=](const down_msg& dm) {
auto i = cached_.begin();
auto e = cached_.end();
while (i != e) {
if (get<1>(i->second) == dm.source)
i = cached_.erase(i);
else
++i;
}
}
};
}
......
......@@ -64,6 +64,10 @@ void ping(event_based_actor* self, size_t num_pings) {
void pong(event_based_actor* self) {
CAF_MESSAGE("pong actor started");
self->set_down_handler([=](down_msg& dm) {
CAF_MESSAGE("received down_msg{" << to_string(dm.reason) << "}");
self->quit(dm.reason);
});
self->become(
[=](ping_atom, int value) -> std::tuple<atom_value, int> {
CAF_MESSAGE("received `ping_atom`");
......@@ -72,10 +76,6 @@ void pong(event_based_actor* self) {
self->become(
[](ping_atom, int val) {
return std::make_tuple(pong_atom::value, val);
},
[=](const down_msg& dm) {
CAF_MESSAGE("received down_msg{" << to_string(dm.reason) << "}");
self->quit(dm.reason);
}
);
// reply to 'ping'
......@@ -102,6 +102,11 @@ void peer_fun(broker* self, connection_handle hdl, const actor& buddy) {
buf.insert(buf.end(), first, first + sizeof(int));
self->flush(hdl);
};
self->set_down_handler([=](down_msg& dm) {
CAF_MESSAGE("received: " << to_string(dm));
if (dm.source == buddy)
self->quit(dm.reason);
});
self->become(
[=](const connection_closed_msg&) {
CAF_MESSAGE("received connection_closed_msg");
......@@ -122,11 +127,6 @@ void peer_fun(broker* self, connection_handle hdl, const actor& buddy) {
[=](pong_atom, int value) {
CAF_MESSAGE("received: pong " << value);
write(pong_atom::value, value);
},
[=](const down_msg& dm) {
CAF_MESSAGE("received: " << to_string(dm));
if (dm.source == buddy)
self->quit(dm.reason);
}
);
}
......
......@@ -60,25 +60,23 @@ behavior make_reflector_behavior(event_based_actor* self) {
using spawn_atom = atom_constant<atom("Spawn")>;
using get_group_atom = atom_constant<atom("GetGroup")>;
struct await_reflector_down_behavior {
event_based_actor* self;
int cnt;
void operator()(const down_msg&) {
if (++cnt == 5)
self->quit();
}
};
struct await_reflector_reply_behavior {
event_based_actor* self;
int cnt;
int downs;
std::vector<actor> vec;
void operator()(const std::string& str, double val) {
CAF_CHECK_EQUAL(str, "Hello reflector!");
CAF_CHECK_EQUAL(val, 5.0);
if (++cnt == 7)
self->become(await_reflector_down_behavior{self, 0});
if (++cnt == 7) {
for (auto actor : vec)
self->monitor(actor);
self->set_down_handler([=](down_msg&) {
if (++downs == 5)
self->quit();
});
}
}
};
......@@ -95,10 +93,7 @@ void make_client_behavior(event_based_actor* self,
};
CAF_CHECK(std::all_of(vec.begin(), vec.end(), is_remote));
self->send(grp, "Hello reflector!", 5.0);
for (auto actor : vec) {
self->monitor(actor);
}
self->become(await_reflector_reply_behavior{self, 0});
self->become(await_reflector_reply_behavior{self, 0, 0, vec});
}
);
}
......
......@@ -70,6 +70,10 @@ behavior ping(event_based_actor* self, size_t num_pings) {
behavior pong(event_based_actor* self) {
CAF_MESSAGE("pong actor started");
self->set_down_handler([=](down_msg& dm) {
CAF_MESSAGE("received down_msg{" << to_string(dm.reason) << "}");
self->quit(dm.reason);
});
return {
[=](ping_atom, int value) -> std::tuple<atom_value, int> {
CAF_MESSAGE("received `ping_atom`");
......@@ -78,10 +82,6 @@ behavior pong(event_based_actor* self) {
self->become(
[](ping_atom, int val) {
return std::make_tuple(pong_atom::value, val);
},
[=](const down_msg& dm) {
CAF_MESSAGE("received down_msg{" << to_string(dm.reason) << "}");
self->quit(dm.reason);
}
);
// reply to 'ping'
......@@ -112,6 +112,11 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl,
buf.insert(buf.end(), first, first + sizeof(int));
self->flush(hdl);
};
self->set_down_handler([=](down_msg& dm) {
CAF_MESSAGE("received down_msg");
if (dm.source == buddy)
self->quit(std::move(dm.reason));
});
return {
[=](const connection_closed_msg&) {
CAF_MESSAGE("received connection_closed_msg");
......@@ -132,12 +137,6 @@ peer::behavior_type peer_fun(peer::broker_pointer self, connection_handle hdl,
[=](pong_atom, int value) {
CAF_MESSAGE("received pong{" << value << "}");
write(pong_atom::value, value);
},
[=](const down_msg& dm) {
CAF_MESSAGE("received down_msg");
if (dm.source == buddy) {
self->quit(dm.reason);
}
}
};
}
......
......@@ -94,14 +94,13 @@ void run_client(int argc, char** argv, uint16_t port) {
port);
CAF_REQUIRE(serv);
scoped_actor self{system};
self->request(serv, infinite, ping{42})
.receive([](const pong& p) { CAF_CHECK_EQUAL(p.value, 42); });
self->request(serv, infinite, ping{42}).receive(
[](const pong& p) {
CAF_CHECK_EQUAL(p.value, 42);
}
);
anon_send_exit(serv, exit_reason::user_shutdown);
self->monitor(serv);
self->receive([&](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_shutdown);
CAF_CHECK_EQUAL(dm.source, serv.address());
});
self->wait_for(serv);
}
void run_server(int argc, char** argv) {
......
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