Commit c91bf3fc authored by Dominik Charousset's avatar Dominik Charousset

API refactoring

parent 3eee072f
......@@ -138,6 +138,7 @@ set(LIBCPPA_SRC
src/binary_serializer.cpp
src/broker.cpp
src/buffer.cpp
src/blocking_untyped_actor.cpp
src/channel.cpp
src/common_actor_ops.cpp
src/context_switching_resume.cpp
......@@ -150,11 +151,12 @@ set(LIBCPPA_SRC
src/deserializer.cpp
src/duration.cpp
src/empty_tuple.cpp
src/event_based_resume.cpp
src/exception.cpp
src/exit_reason.cpp
src/fd_util.cpp
src/fiber.cpp
src/functor_based_actor.cpp
src/functor_based_blocking_actor.cpp
src/get_root_uuid.cpp
src/get_mac_addresses.cpp
src/group.cpp
......@@ -178,11 +180,10 @@ set(LIBCPPA_SRC
src/partial_function.cpp
src/primitive_variant.cpp
src/ref_counted.cpp
src/resumable.cpp
src/remote_actor_proxy.cpp
src/response_handle.cpp
src/ripemd_160.cpp
src/scheduled_actor.cpp
src/scheduled_actor_dummy.cpp
src/scheduler.cpp
src/scoped_actor.cpp
src/serializer.cpp
......@@ -190,13 +191,13 @@ set(LIBCPPA_SRC
src/singleton_manager.cpp
src/string_serialization.cpp
src/sync_request_bouncer.cpp
src/thread_mapped_actor.cpp
src/thread_pool_scheduler.cpp
src/to_uniform_name.cpp
src/type_lookup_table.cpp
src/unicast_network.cpp
src/uniform_type_info.cpp
src/uniform_type_info_map.cpp
src/untyped_actor.cpp
src/weak_ptr_anchor.cpp
src/yield_interface.cpp)
......
......@@ -13,7 +13,6 @@ cppa/attachable.hpp
cppa/behavior.hpp
cppa/binary_deserializer.hpp
cppa/binary_serializer.hpp
cppa/blocking_actor.hpp
cppa/blocking_untyped_actor.hpp
cppa/channel.hpp
cppa/common_actor_ops.hpp
......@@ -37,7 +36,6 @@ cppa/detail/demangle.hpp
cppa/detail/disablable_delete.hpp
cppa/detail/empty_tuple.hpp
cppa/detail/fd_util.hpp
cppa/detail/get_behavior.hpp
cppa/detail/group_manager.hpp
cppa/detail/handle.hpp
cppa/detail/ieee_754.hpp
......@@ -53,7 +51,6 @@ cppa/detail/projection.hpp
cppa/detail/pseudo_tuple.hpp
cppa/detail/ptype_to_type.hpp
cppa/detail/raw_access.hpp
cppa/detail/scheduled_actor_dummy.hpp
cppa/detail/serialize_tuple.hpp
cppa/detail/singleton_manager.hpp
cppa/detail/singleton_mixin.hpp
......@@ -105,7 +102,6 @@ cppa/io/remote_actor_proxy.hpp
cppa/io/stream.hpp
cppa/local_actor.hpp
cppa/logging.hpp
cppa/mailbox_based.hpp
cppa/mailbox_element.hpp
cppa/match.hpp
cppa/match_expr.hpp
......@@ -117,7 +113,6 @@ cppa/message_header.hpp
cppa/message_id.hpp
cppa/message_priority.hpp
cppa/node_id.hpp
cppa/nonblocking_actor.hpp
cppa/none.hpp
cppa/object.hpp
cppa/on.hpp
......@@ -133,7 +128,7 @@ cppa/opt.hpp
cppa/optional.hpp
cppa/optional_variant.hpp
cppa/partial_function.hpp
cppa/policy/invoke_policy_base.hpp
cppa/policy/invoke_policy.hpp
cppa/policy/nestable_invoke.hpp
cppa/policy/prioritizing.hpp
cppa/policy/sequential_invoke.hpp
......@@ -144,7 +139,6 @@ cppa/ref_counted.hpp
cppa/replies_to.hpp
cppa/response_handle.hpp
cppa/sb_actor.hpp
cppa/scheduled_actor.hpp
cppa/scheduler.hpp
cppa/scoped_actor.hpp
cppa/send.hpp
......@@ -152,7 +146,6 @@ cppa/serializer.hpp
cppa/singletons.hpp
cppa/spawn.hpp
cppa/spawn_options.hpp
cppa/thread_mapped_actor.hpp
cppa/timeout_definition.hpp
cppa/to_string.hpp
cppa/tpartial_function.hpp
......@@ -245,7 +238,6 @@ src/demangle.cpp
src/deserializer.cpp
src/duration.cpp
src/empty_tuple.cpp
src/event_based_resume.cpp
src/exception.cpp
src/exit_reason.cpp
src/factory.cpp
......@@ -286,8 +278,6 @@ src/ref_counted.cpp
src/remote_actor_proxy.cpp
src/response_handle.cpp
src/ripemd_160.cpp
src/scheduled_actor.cpp
src/scheduled_actor_dummy.cpp
src/scheduler.cpp
src/scoped_actor.cpp
src/send.cpp
......@@ -296,7 +286,6 @@ src/shared_spinlock.cpp
src/singleton_manager.cpp
src/string_serialization.cpp
src/sync_request_bouncer.cpp
src/thread_mapped_actor.cpp
src/thread_pool_scheduler.cpp
src/to_uniform_name.cpp
src/type_lookup_table.cpp
......@@ -338,3 +327,14 @@ cppa/policy/resume_policy.hpp
cppa/policy/cooperative_scheduling.hpp
cppa/policy/scheduling_policy.hpp
cppa/policy/priority_policy.hpp
cppa/policy/not_prioritizing.hpp
cppa/detail/functor_based_blocking_actor.hpp
src/functor_based_actor.cpp
cppa/mailbox_based.hpp
cppa/behavior_stack_based.hpp
cppa/resumable.hpp
src/resumable.cpp
cppa/policy/middleman_scheduling.hpp
src/untyped_actor.cpp
src/functor_based_blocking_actor.cpp
src/blocking_untyped_actor.cpp
......@@ -143,6 +143,12 @@ class abstract_actor : public abstract_channel {
*/
inline const node_id& node() const;
/**
* @brief Returns the actor's exit reason of
* <tt>exit_reason::not_exited</tt> if it's still alive.
*/
inline std::uint32_t exit_reason() const;
protected:
abstract_actor();
......@@ -166,12 +172,6 @@ class abstract_actor : public abstract_channel {
*/
bool unlink_from_impl(const actor_addr& other);
/**
* @brief Returns the actor's exit reason of
* <tt>exit_reason::not_exited</tt> if it's still alive.
*/
inline std::uint32_t exit_reason() const;
/**
* @brief Returns <tt>exit_reason() != exit_reason::not_exited</tt>.
*/
......
......@@ -45,6 +45,10 @@ namespace cppa {
namespace detail { class raw_access; }
struct invalid_actor_t { constexpr invalid_actor_t() { } };
constexpr invalid_actor_t invalid_actor = invalid_actor_t{};
/**
* @brief Identifies an untyped actor.
*/
......@@ -65,6 +69,8 @@ class actor : util::comparable<actor> {
inline bool operator!() const;
actor(const invalid_actor_t&);
void enqueue(const message_header& hdr, any_tuple msg) const;
inline common_actor_ops* operator->() const {
......@@ -89,7 +95,7 @@ inline actor::operator bool() const {
inline bool actor::operator!() const {
return !static_cast<bool>(*this);
}
} // namespace cppa
#endif // CPPA_ACTOR_HPP
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_ACTOR_ADDR_HPP
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_ACTOR_NAMESPACE_HPP
......
......@@ -28,12 +28,9 @@
\******************************************************************************/
#ifndef CPPA_STACKLESS_HPP
#define CPPA_STACKLESS_HPP
#ifndef CPPA_BEHAVIOR_STACK_BASED_HPP
#define CPPA_BEHAVIOR_STACK_BASED_HPP
#include "cppa/util/dptr.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/behavior_stack.hpp"
namespace cppa {
......@@ -41,14 +38,14 @@ namespace cppa {
#ifdef CPPA_DOCUMENTATION
/**
* @brief Policy tag that causes {@link event_based_actor::become} to
* @brief Policy tag that causes {@link untyped_actor::become} to
* discard the current behavior.
* @relates local_actor
*/
constexpr auto discard_behavior;
/**
* @brief Policy tag that causes {@link event_based_actor::become} to
* @brief Policy tag that causes {@link untyped_actor::become} to
* keep the current behavior available.
* @relates local_actor
*/
......@@ -75,25 +72,14 @@ constexpr keep_behavior_t keep_behavior = keep_behavior_t{};
#endif
template<class Base, class Subtype>
class stackless : public Base {
protected:
typedef stackless combined_type;
class behavior_stack_based : public Base {
public:
template<typename... Ts>
stackless(Ts&&... args) : Base(std::forward<Ts>(args)...) { }
typedef behavior_stack_based combined_type;
static constexpr auto receive_flag = detail::rp_sequential;
bool has_behavior() {
return this->m_bhvr_stack.empty() == false;
}
void unbecome() {
this->m_bhvr_stack.pop_async_back();
inline void unbecome() {
m_bhvr_stack.pop_async_back();
}
/**
......@@ -110,49 +96,35 @@ class stackless : public Base {
std::forward<Ts>(args)...),
true);
}
template<bool Discard, typename... Ts>
inline void become(behavior_policy<Discard>, Ts&&... args) {
do_become(match_expr_convert(std::forward<Ts>(args)...), Discard);
}
void become_waiting_for(behavior bhvr, message_id mf) {
if (bhvr.timeout().valid()) {
this->reset_timeout();
this->request_timeout(bhvr.timeout());
//FIXME
}
this->m_bhvr_stack.push_back(std::move(bhvr), mf);
m_bhvr_stack.push_back(std::move(bhvr), mf);
}
void do_become(behavior&& bhvr, bool discard_old) {
this->reset_timeout();
this->request_timeout(bhvr.timeout());
if (discard_old) this->m_bhvr_stack.pop_async_back();
this->m_bhvr_stack.push_back(std::move(bhvr));
if (discard_old) m_bhvr_stack.pop_async_back();
m_bhvr_stack.push_back(std::move(bhvr));
}
inline bool has_behavior() const {
return this->m_bhvr_stack.empty() == false;
return m_bhvr_stack.empty() == false;
}
inline behavior& get_behavior() {
CPPA_REQUIRE(this->m_bhvr_stack.empty() == false);
return this->m_bhvr_stack.back();
CPPA_REQUIRE(m_bhvr_stack.empty() == false);
return m_bhvr_stack.back();
}
inline void handle_timeout(behavior& bhvr) {
CPPA_REQUIRE(bhvr.timeout().valid());
this->reset_timeout();
bhvr.handle_timeout();
if (this->m_bhvr_stack.empty() == false) {
this->request_timeout(get_behavior().timeout());
}
}
void exec_bhvr_stack() {
while (!m_bhvr_stack.empty()) {
m_bhvr_stack.exec(m_recv_policy, util::dptr<Subtype>(this));
}
}
inline detail::behavior_stack& bhvr_stack() {
......@@ -168,11 +140,8 @@ class stackless : public Base {
// allows actors to keep previous behaviors and enables unbecome()
detail::behavior_stack m_bhvr_stack;
// used for message handling in subclasses
detail::receive_policy m_recv_policy;
};
} // namespace cppa
#endif // CPPA_STACKLESS_HPP
#endif // CPPA_BEHAVIOR_STACK_BASED_HPP
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_STACKED_ACTOR_MIXIN_HPP
#define CPPA_STACKED_ACTOR_MIXIN_HPP
#include <chrono>
#include <memory>
#include <functional>
#include "cppa/behavior.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/mailbox_element.hpp"
namespace cppa {
class blocking_actor : public local_actor {
friend class detail::receive_policy;
friend class detail::behavior_stack;
protected:
typedef stacked combined_type;
public:
static constexpr auto receive_flag = detail::rp_nestable;
struct receive_while_helper {
std::function<void(behavior&)> m_dq;
std::function<bool()> m_stmt;
template<typename... Ts>
void operator()(Ts&&... args) {
static_assert(sizeof...(Ts) > 0,
"operator() requires at least one argument");
behavior bhvr = match_expr_convert(std::forward<Ts>(args)...);
while (m_stmt()) m_dq(bhvr);
}
};
template<typename T>
struct receive_for_helper {
std::function<void(behavior&)> m_dq;
T& begin;
T end;
template<typename... Ts>
void operator()(Ts&&... args) {
behavior bhvr = match_expr_convert(std::forward<Ts>(args)...);
for ( ; begin != end; ++begin) m_dq(bhvr);
}
};
struct do_receive_helper {
std::function<void(behavior&)> m_dq;
behavior m_bhvr;
template<typename Statement>
void until(Statement stmt) {
do { m_dq(m_bhvr); }
while (stmt() == false);
}
};
/**
* @brief Dequeues the next message from the mailbox that
* is matched by given behavior.
*/
template<typename... Ts>
void receive(Ts&&... args) {
static_assert(sizeof...(Ts), "receive() requires at least one argument");
dequeue(match_expr_convert(std::forward<Ts>(args)...));
}
/**
* @brief Receives messages in an endless loop.
* Semantically equal to: <tt>for (;;) { receive(...); }</tt>
*/
template<typename... Ts>
void receive_loop(Ts&&... args) {
behavior bhvr = match_expr_convert(std::forward<Ts>(args)...);
for (;;) dequeue(bhvr);
}
/**
* @brief Receives messages as in a range-based loop.
*
* Semantically equal to:
* <tt>for ( ; begin != end; ++begin) { receive(...); }</tt>.
*
* <b>Usage example:</b>
* @code
* int i = 0;
* receive_for(i, 10) (
* on(atom("get")) >> [&]() -> any_tuple { return {"result", i}; }
* );
* @endcode
* @param begin First value in range.
* @param end Last value in range (excluded).
* @returns A functor implementing the loop.
*/
template<typename T>
receive_for_helper<T> receive_for(T& begin, const T& end) {
return {make_dequeue_callback(), begin, end};
}
/**
* @brief Receives messages as long as @p stmt returns true.
*
* Semantically equal to: <tt>while (stmt()) { receive(...); }</tt>.
*
* <b>Usage example:</b>
* @code
* int i = 0;
* receive_while([&]() { return (++i <= 10); })
* (
* on<int>() >> int_fun,
* on<float>() >> float_fun
* );
* @endcode
* @param stmt Lambda expression, functor or function returning a @c bool.
* @returns A functor implementing the loop.
*/
template<typename Statement>
receive_while_helper receive_while(Statement stmt) {
static_assert(std::is_same<bool, decltype(stmt())>::value,
"functor or function does not return a boolean");
return {make_dequeue_callback(), stmt};
}
/**
* @brief Receives messages until @p stmt returns true.
*
* Semantically equal to: <tt>do { receive(...); } while (stmt() == false);</tt>
*
* <b>Usage example:</b>
* @code
* int i = 0;
* do_receive
* (
* on<int>() >> int_fun,
* on<float>() >> float_fun
* )
* .until([&]() { return (++i >= 10); };
* @endcode
* @param bhvr Denotes the actor's response the next incoming message.
* @returns A functor providing the @c until member function.
*/
template<typename... Ts>
do_receive_helper do_receive(Ts&&... args) {
return { make_dequeue_callback()
, match_expr_convert(std::forward<Ts>(args)...)};
}
virtual void run() {
if (m_behavior) m_behavior();
auto dthis = util::dptr<Subtype>(this);
auto rsn = dthis->planned_exit_reason();
dthis->cleanup(rsn == exit_reason::not_exited ? exit_reason::normal : rsn);
}
inline void set_behavior(std::function<void()> fun) {
m_behavior = std::move(fun);
}
inline optional<behavior&> sync_handler(message_id msg_id) {
auto i = m_sync_handler.find(msg_id);
if (i != m_sync_handler.end()) return i->second;
return none;
}
protected:
template<typename... Ts>
stacked(Ts&&... args) : Base(std::forward<Ts>(args)...) { }
virtual bool has_behavior() {
return static_cast<bool>(m_behavior);
}
std::function<void()> m_behavior;
detail::receive_policy m_recv_policy;
private:
std::map<message_id, behavior> m_sync_handler;
std::function<void(behavior&)> make_dequeue_callback() {
return [=](behavior& bhvr) { dequeue(bhvr); };
}
void dequeue(behavior& bhvr) {
m_recv_policy.receive(util::dptr<Subtype>(this), bhvr);
}
void dequeue_response(behavior& bhvr, message_id request_id) {
m_recv_policy.receive(util::dptr<Subtype>(this), bhvr, request_id);
}
};
} // namespace cppa
#endif // CPPA_STACKED_ACTOR_MIXIN_HPP
......@@ -25,23 +25,210 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_BLOCKING_UNTYPED_ACTOR_HPP
#define CPPA_BLOCKING_UNTYPED_ACTOR_HPP
#include "cppa/extend.hpp"
#include "cppa/stacked.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/mailbox_element.hpp"
namespace cppa {
/**
* @extends local_actor
*/
class blocking_untyped_actor : public extend<local_actor>::with<stacked> {
class blocking_untyped_actor : public extend<local_actor>::with<mailbox_based> {
public:
typedef std::chrono::high_resolution_clock::time_point timeout_type;
struct receive_while_helper {
std::function<void(behavior&)> m_dq;
std::function<bool()> m_stmt;
template<typename... Ts>
void operator()(Ts&&... args) {
static_assert(sizeof...(Ts) > 0,
"operator() requires at least one argument");
behavior bhvr = match_expr_convert(std::forward<Ts>(args)...);
while (m_stmt()) m_dq(bhvr);
}
};
template<typename T>
struct receive_for_helper {
std::function<void(behavior&)> m_dq;
T& begin;
T end;
template<typename... Ts>
void operator()(Ts&&... args) {
behavior bhvr = match_expr_convert(std::forward<Ts>(args)...);
for ( ; begin != end; ++begin) m_dq(bhvr);
}
};
struct do_receive_helper {
std::function<void(behavior&)> m_dq;
behavior m_bhvr;
template<typename Statement>
void until(Statement stmt) {
do { m_dq(m_bhvr); }
while (stmt() == false);
}
};
/**
* @brief Dequeues the next message from the mailbox that
* is matched by given behavior.
*/
template<typename... Ts>
void receive(Ts&&... args) {
static_assert(sizeof...(Ts), "receive() requires at least one argument");
dequeue(match_expr_convert(std::forward<Ts>(args)...));
}
/**
* @brief Receives messages in an endless loop.
* Semantically equal to: <tt>for (;;) { receive(...); }</tt>
*/
template<typename... Ts>
void receive_loop(Ts&&... args) {
behavior bhvr = match_expr_convert(std::forward<Ts>(args)...);
for (;;) dequeue(bhvr);
}
/**
* @brief Receives messages as in a range-based loop.
*
* Semantically equal to:
* <tt>for ( ; begin != end; ++begin) { receive(...); }</tt>.
*
* <b>Usage example:</b>
* @code
* int i = 0;
* receive_for(i, 10) (
* on(atom("get")) >> [&]() -> any_tuple { return {"result", i}; }
* );
* @endcode
* @param begin First value in range.
* @param end Last value in range (excluded).
* @returns A functor implementing the loop.
*/
template<typename T>
receive_for_helper<T> receive_for(T& begin, const T& end) {
return {make_dequeue_callback(), begin, end};
}
/**
* @brief Receives messages as long as @p stmt returns true.
*
* Semantically equal to: <tt>while (stmt()) { receive(...); }</tt>.
*
* <b>Usage example:</b>
* @code
* int i = 0;
* receive_while([&]() { return (++i <= 10); })
* (
* on<int>() >> int_fun,
* on<float>() >> float_fun
* );
* @endcode
* @param stmt Lambda expression, functor or function returning a @c bool.
* @returns A functor implementing the loop.
*/
template<typename Statement>
receive_while_helper receive_while(Statement stmt) {
static_assert(std::is_same<bool, decltype(stmt())>::value,
"functor or function does not return a boolean");
return {make_dequeue_callback(), stmt};
}
/**
* @brief Handles a synchronous response message in an event-based way.
* @param handle A future for a synchronous response.
* @throws std::logic_error if @p handle is not valid or if the actor
* already received the response for @p handle
* @relates message_future
*/
inline sync_receive_helper receive_response(const message_future& f) {
return {f};
}
/**
* @brief Receives messages until @p stmt returns true.
*
* Semantically equal to: <tt>do { receive(...); } while (stmt() == false);</tt>
*
* <b>Usage example:</b>
* @code
* int i = 0;
* do_receive
* (
* on<int>() >> int_fun,
* on<float>() >> float_fun
* )
* .until([&]() { return (++i >= 10); };
* @endcode
* @param bhvr Denotes the actor's response the next incoming message.
* @returns A functor providing the @c until member function.
*/
template<typename... Ts>
do_receive_helper do_receive(Ts&&... args) {
return { make_dequeue_callback()
, match_expr_convert(std::forward<Ts>(args)...)};
}
inline optional<behavior&> sync_handler(message_id msg_id) {
auto i = m_sync_handler.find(msg_id);
if (i != m_sync_handler.end()) return i->second;
return none;
}
inline void dequeue(behavior&& bhvr) {
behavior tmp{std::move(bhvr)};
dequeue(tmp);
}
virtual void dequeue(behavior& bhvr) = 0;
virtual mailbox_element* dequeue() = 0;
virtual mailbox_element* try_dequeue() = 0;
virtual mailbox_element* try_dequeue(const timeout_type&) = 0;
void await_all_other_actors_done();
virtual void act() = 0;
private:
std::map<message_id, behavior> m_sync_handler;
std::function<void(behavior&)> make_dequeue_callback() {
return [=](behavior& bhvr) { dequeue(bhvr); };
}
/*
void dequeue_response(behavior& bhvr, message_id request_id) {
m_recv_policy.receive(util::dptr<Subtype>(this), bhvr, request_id);
}
*/
};
} // namespace cppa
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_CHANNEL_HPP
......
......@@ -46,7 +46,6 @@
#include "cppa/behavior.hpp"
#include "cppa/announce.hpp"
#include "cppa/sb_actor.hpp"
#include "cppa/threaded.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/to_string.hpp"
#include "cppa/any_tuple.hpp"
......@@ -56,14 +55,12 @@
#include "cppa/typed_actor.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/prioritizing.hpp"
#include "cppa/scoped_actor.hpp"
#include "cppa/spawn_options.hpp"
#include "cppa/untyped_actor.hpp"
#include "cppa/abstract_actor.hpp"
#include "cppa/message_future.hpp"
#include "cppa/response_handle.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/blocking_untyped_actor.hpp"
#include "cppa/util/type_traits.hpp"
......@@ -79,7 +76,6 @@
#include "cppa/io/connection_handle.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/get_behavior.hpp"
#include "cppa/detail/actor_registry.hpp"
/**
......@@ -436,8 +432,7 @@
namespace cppa {
template<typename... Ts>
void send_tuple_as(const actor& from, const channel& to, any_tuple msg) {
inline void send_tuple_as(const actor& from, const channel& to, any_tuple msg) {
to.enqueue({from->address(), to}, std::move(msg));
}
......@@ -450,6 +445,23 @@ template<typename... Ts>
void send_as(const actor& from, const channel& to, Ts&&... args) {
send_tuple_as(from, to, make_any_tuple(std::forward<Ts>(args)...));
}
inline void anon_send_tuple(const channel& to, any_tuple msg) {
to.enqueue({invalid_actor_addr, to}, std::move(msg));
}
/**
* @brief Anonymously sends a message to @p receiver;
*/
template<typename... Ts>
inline void anon_send(const channel& receiver, Ts&&... args) {
anon_send_tuple(receiver, make_any_tuple(std::forward<Ts>(args)...));
}
inline void anon_send_exit(const actor_addr&, std::uint32_t) {
}
/**
* @brief Blocks execution of this actor until all
* other actors finished execution.
......@@ -517,10 +529,16 @@ template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
actor spawn_io(io::input_stream_ptr in,
io::output_stream_ptr out,
Ts&&... args) {
using namespace io;
using namespace std;
auto ptr = make_counted<Impl>(move(in), move(out), forward<Ts>(args)...);
return eval_sopts(Options, io::init_and_launch(move(ptr)));
using namespace policy;
using proper_impl = detail::proper_actor<Impl,
middleman_scheduling,
not_prioritizing,
no_resume,
cooperative_scheduling>;
auto ptr = make_counted<proper_impl>(std::move(in), std::move(out),
std::forward<Ts>(args)...);
ptr->launch();
return ptr;
}
/**
......@@ -530,16 +548,12 @@ actor spawn_io(io::input_stream_ptr in,
* @returns An {@link actor_ptr} to the spawned {@link actor}.
*/
template<spawn_options Options = no_spawn_options,
typename F = std::function<void (io::broker*)>,
typename... Ts>
typename F = std::function<void (io::broker*)>>
actor spawn_io(F fun,
io::input_stream_ptr in,
io::output_stream_ptr out,
Ts&&... args) {
using namespace std;
auto ptr = io::broker::from(move(fun), move(in), move(out),
forward<Ts>(args)...);
return eval_sopts(Options, io::init_and_launch(move(ptr)));
io::output_stream_ptr out) {
return spawn_io<io::default_broker>(std::move(fun), std::move(in),
std::move(out));
}
/*
......@@ -551,21 +565,17 @@ actor_ptr spawn_io(const char* host, uint16_t port, Ts&&... args) {
*/
template<spawn_options Options = no_spawn_options,
typename F = std::function<void (io::broker*)>,
typename... Ts>
actor spawn_io(F fun, const std::string& host, uint16_t port, Ts&&... args) {
typename F = std::function<void (io::broker*)>>
actor spawn_io(F fun, const std::string& host, uint16_t port) {
auto ptr = io::ipv4_io_stream::connect_to(host.c_str(), port);
return spawn_io(std::move(fun), ptr, ptr, std::forward<Ts>(args)...);
return spawn_io(std::move(fun), ptr, ptr);
}
template<spawn_options Options = no_spawn_options,
typename F = std::function<void (io::broker*)>,
typename... Ts>
actor spawn_io_server(F fun, uint16_t port, Ts&&... args) {
typename F = std::function<void (io::broker*)>>
actor spawn_io_server(F fun, uint16_t port) {
using namespace std;
auto ptr = io::broker::from(move(fun), io::ipv4_acceptor::create(port),
forward<Ts>(args)...);
return eval_sopts(Options, io::init_and_launch(move(ptr)));
return spawn_io(move(fun), io::ipv4_acceptor::create(port));
}
/**
......
......@@ -31,13 +31,100 @@
#ifndef CPPA_FUNCTOR_BASED_ACTOR_HPP
#define CPPA_FUNCTOR_BASED_ACTOR_HPP
#include <type_traits>
#include "cppa/untyped_actor.hpp"
namespace cppa { namespace detail {
class functor_based_actor : public untyped_actor {
public:
typedef std::function<behavior(untyped_actor*)> make_behavior_fun;
typedef std::function<void(untyped_actor*)> void_fun;
template<typename F, typename... Ts>
functor_based_actor(F f, Ts&&... vs) {
untyped_actor* dummy = nullptr;
create(dummy, f, std::forward<Ts>(vs)...);
}
behavior make_behavior() override;
private:
void create(untyped_actor*, void_fun);
template<class Actor, typename F, typename... Ts>
auto create(Actor*, F f, Ts&&... vs) ->
typename std::enable_if<
std::is_convertible<
decltype(f(std::forward<Ts>(vs)...)),
behavior
>::value
>::type {
auto fun = std::bind(f, std::forward<Ts>(vs)...);
m_make_behavior = [fun](Actor*) -> behavior { return fun(); };
}
template<class Actor, typename F, typename... Ts>
auto create(Actor* dummy, F f, Ts&&... vs) ->
typename std::enable_if<
std::is_convertible<
decltype(f(dummy, std::forward<Ts>(vs)...)),
behavior
>::value
>::type {
auto fun = std::bind(f, std::placeholders::_1, std::forward<Ts>(vs)...);
m_make_behavior = [fun](Actor* self) -> behavior { return fun(self); };
}
template<class Actor, typename F, typename... Ts>
auto create(Actor*, F f, Ts&&... vs) ->
typename std::enable_if<
std::is_same<
decltype(f(std::forward<Ts>(vs)...)),
void
>::value
>::type {
std::function<void()> fun = std::bind(f, std::forward<Ts>(vs)...);
m_make_behavior = [fun](Actor*) -> behavior {
fun();
return behavior{};
};
}
template<class Actor, typename F, typename... Ts>
auto create(Actor* dummy, F f, Ts&&... vs) ->
typename std::enable_if<
std::is_same<
decltype(f(dummy, std::forward<Ts>(vs)...)),
void
>::value
>::type {
std::function<void(Actor*)> fun = std::bind(f, std::placeholders::_1,
std::forward<Ts>(vs)...);
m_make_behavior = [fun](Actor* self) -> behavior {
fun(self);
return behavior{};
};
}
/*
template<class Actor, typename F, typename T0, typename... Ts>
void create(void*, F, T0&&, Ts&&...) {
// this overload acts as 'catch-all' rule to
// give the user a clear hint at what's wrong
// with the provided functor
static_assert(sizeof...(Ts) != sizeof...(Ts),
"spawn: provided functor must either return 'void'' "
"or 'cppa::behavior'");
}
*/
make_behavior_fun m_make_behavior;
};
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef FUNCTOR_BASED_BLOCKING_ACTOR_HPP
#define FUNCTOR_BASED_BLOCKING_ACTOR_HPP
#include "cppa/blocking_untyped_actor.hpp"
namespace cppa {
namespace detail {
class functor_based_blocking_actor : public blocking_untyped_actor {
public:
typedef std::function<void (blocking_untyped_actor*)> act_fun;
template<typename F, typename... Ts>
functor_based_blocking_actor(F f, Ts&&... vs) {
blocking_untyped_actor* dummy = nullptr;
create(dummy, f, std::forward<Ts>(vs)...);
}
protected:
void act() override;
private:
void create(blocking_untyped_actor*, act_fun);
template<class Actor, typename F, typename T0, typename... Ts>
auto create(Actor* dummy, F f, T0&& v0, Ts&&... vs) ->
typename std::enable_if<
std::is_same<
decltype(f(dummy, std::forward<T0>(v0), std::forward<Ts>(vs)...)),
void
>::value
>::type {
create(dummy, std::bind(f, std::placeholders::_1,
std::forward<T0>(v0), std::forward<Ts>(vs)...));
}
template<class Actor, typename F, typename T0, typename... Ts>
auto create(Actor* dummy, F f, T0&& v0, Ts&&... vs) ->
typename std::enable_if<
std::is_same<
decltype(f(std::forward<T0>(v0), std::forward<Ts>(vs)...)),
void
>::value
>::type {
std::function<void()> fun = std::bind(f, std::forward<T0>(v0),
std::forward<Ts>(vs)...);
create(dummy, [fun](Actor*) { fun(); });
}
act_fun m_act;
};
} // namespace detail
} // namespace cppa
#endif // FUNCTOR_BASED_BLOCKING_ACTOR_HPP
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_GET_BEHAVIOR_HPP
#define CPPA_GET_BEHAVIOR_HPP
#include <type_traits>
#include "cppa/util/call.hpp"
#include "cppa/util/int_list.hpp"
#include "cppa/util/type_traits.hpp"
#include "cppa/detail/tdata.hpp"
#include "cppa/scheduled_actor.hpp"
namespace cppa { namespace detail {
// default: <true, false, F>
template<bool IsFunctionPtr, bool HasArguments, typename F, typename... Ts>
class ftor_behavior : public scheduled_actor {
F m_fun;
public:
ftor_behavior(F ptr) : m_fun(ptr) { }
virtual void act() { m_fun(); }
};
template<typename F, typename... Ts>
class ftor_behavior<true, true, F, Ts...> : public scheduled_actor {
static_assert(sizeof...(Ts) > 0, "sizeof...(Ts) == 0");
F m_fun;
typedef typename tdata_from_type_list<
typename util::tl_map<util::type_list<Ts...>,
implicit_conversions>::type>::type
tdata_type;
tdata_type m_args;
public:
ftor_behavior(F ptr, const Ts&... args) : m_fun(ptr), m_args(args...) { }
virtual void act() {
util::apply_args(m_fun, m_args, util::get_indices(m_args));
}
};
template<typename F>
class ftor_behavior<false, false, F> : public scheduled_actor {
F m_fun;
public:
ftor_behavior(const F& arg) : m_fun(arg) { }
ftor_behavior(F&& arg) : m_fun(std::move(arg)) { }
virtual void act() { m_fun(); }
};
template<typename F, typename... Ts>
class ftor_behavior<false, true, F, Ts...> : public scheduled_actor {
static_assert(sizeof...(Ts) > 0, "sizeof...(Ts) == 0");
F m_fun;
typedef typename tdata_from_type_list<
typename util::tl_map<util::type_list<Ts...>,
implicit_conversions>::type>::type
tdata_type;
tdata_type m_args;
public:
ftor_behavior(const F& f, const Ts&... args) : m_fun(f), m_args(args...) {
}
ftor_behavior(F&& f, const Ts&... args)
: m_fun(std::move(f)), m_args(args...) { }
virtual void act() {
util::apply_args(m_fun, m_args, util::get_indices(m_args));
}
};
template<typename R>
scheduled_actor* get_behavior(std::integral_constant<bool, true>, R (*fptr)()) {
static_assert(std::is_convertible<R, scheduled_actor*>::value == false,
"Spawning a function returning an actor_behavior? "
"Are you sure that you do not want to spawn the behavior "
"returned by that function?");
return new ftor_behavior<true, false, R (*)()>(fptr);
}
template<typename F>
scheduled_actor* get_behavior(std::integral_constant<bool, false>, F&& ftor) {
static_assert(std::is_convertible<decltype(ftor()), scheduled_actor*>::value == false,
"Spawning a functor returning an actor_behavior? "
"Are you sure that you do not want to spawn the behavior "
"returned by that functor?");
typedef typename util::rm_const_and_ref<F>::type ftype;
return new ftor_behavior<false, false, ftype>(std::forward<F>(ftor));
}
template<typename F, typename T, typename... Ts>
scheduled_actor* get_behavior(std::true_type, F fptr, const T& arg, const Ts&... args) {
static_assert(std::is_convertible<decltype(fptr(arg, args...)), scheduled_actor*>::value == false,
"Spawning a function returning an actor_behavior? "
"Are you sure that you do not want to spawn the behavior "
"returned by that function?");
typedef ftor_behavior<true, true, F, T, Ts...> impl;
return new impl(fptr, arg, args...);
}
template<typename F, typename T, typename... Ts>
scheduled_actor* get_behavior(std::false_type, F ftor, const T& arg, const Ts&... args) {
static_assert(std::is_convertible<decltype(ftor(arg, args...)), scheduled_actor*>::value == false,
"Spawning a functor returning an actor_behavior? "
"Are you sure that you do not want to spawn the behavior "
"returned by that functor?");
typedef typename util::rm_const_and_ref<F>::type ftype;
typedef ftor_behavior<false, true, ftype, T, Ts...> impl;
return new impl(std::forward<F>(ftor), arg, args...);
}
} } // namespace cppa::detail
#endif // CPPA_GET_BEHAVIOR_HPP
......@@ -3,37 +3,46 @@
#include <type_traits>
#include "cppa/blocking_actor.hpp"
#include "cppa/resumable.hpp"
#include "cppa/mailbox_element.hpp"
#include "cppa/blocking_untyped_actor.hpp"
namespace cppa { namespace util { class fiber; } }
#include "cppa/util/duration.hpp"
namespace cppa { namespace detail {
namespace cppa {
namespace util {
struct fiber;
} // namespace util
} // namespace cppa
namespace cppa {
namespace detail {
template<class Base,
class SchedulingPolicy,
class PriorityPolicy,
class ResumePolicy,
class InvokePolicy,
bool OverrideDequeue = std::is_base_of<blocking_actor, Base>::value>
class proper_actor : public Base {
bool OverrideDequeue = std::is_base_of<blocking_untyped_actor, Base>::value>
class proper_actor : public ResumePolicy::template mixin<Base> {
friend SchedulingPolicy;
friend PriorityPolicy;
friend ResumePolicy;
friend InvokePolicy;
typedef typename ResumePolicy::template mixin<Base> super;
public:
template<typename... Ts>
proper_actor(Ts&&... args) : Base(std::forward<Ts>(args)) { }
template <typename... Ts>
proper_actor(Ts&&... args) : super(std::forward<Ts>(args)...) { }
void enqueue(const message_header& hdr, any_tuple msg) override {
m_scheduling_policy.enqueue(this, hdr, msg);
}
void resume(util::fiber* fb) override {
m_resume_policy.resume(this, fb);
}
inline void launch() {
m_scheduling_policy.launch(this);
}
inline void launch() { m_scheduling_policy.launch(this); }
inline mailbox_element* next_message() {
return m_priority_policy.next_message(this);
......@@ -44,74 +53,113 @@ class proper_actor : public Base {
}
// grant access to the actor's mailbox
Base::mailbox_type& mailbox() {
return this->m_mailbox;
typename Base::mailbox_type& mailbox() { return this->m_mailbox; }
SchedulingPolicy& scheduling_policy() { return m_scheduling_policy; }
PriorityPolicy& priority_policy() { return m_priority_policy; }
ResumePolicy& resume_policy() { return m_resume_policy; }
InvokePolicy& invoke_policy() { return m_invoke_policy; }
inline void push_timeout() {
m_scheduling_policy.push_timeout();
}
inline void current_node(mailbox_element* ptr) {
this->m_current_node = ptr;
}
inline void request_timeout(const util::duration& rel_timeout) {
m_invoke_policy.request_timeout(this, rel_timeout);
}
inline mailbox_element* current_node() { return this->m_current_node; }
detail::behavior_stack& bhvr_stack() { return this->m_bhvr_stack; }
protected:
SchedulingPolicy m_scheduling_policy;
PriorityPolicy m_priority_policy;
ResumePolicy m_resume_policy;
InvokePolicy m_invoke_policy;
PriorityPolicy m_priority_policy;
ResumePolicy m_resume_policy;
InvokePolicy m_invoke_policy;
};
// for blocking actors, there's one more member function to implement
template<class Base,
class SchedulingPolicy,
class PriorityPolicy,
class ResumePolicy,
class InvokePolicy>
class proper_actor<Base,
SchedulingPolicy,
PriorityPolicy,
ResumePolicy,
InvokePolicy,
true> : public proper_actor<Base,
SchedulingPolicy,
PriorityPolicy,
ResumePolicy,
InvokePolicy,
false> {
template <class Base, class SchedulingPolicy, class PriorityPolicy,
class ResumePolicy, class InvokePolicy>
class proper_actor<
Base, SchedulingPolicy, PriorityPolicy, ResumePolicy, InvokePolicy,
true> final : public proper_actor<Base, SchedulingPolicy, PriorityPolicy,
ResumePolicy, InvokePolicy, false> {
typedef proper_actor<Base, SchedulingPolicy, PriorityPolicy,
ResumePolicy, InvokePolicy, false>
super;
public:
template <typename... Ts>
proper_actor(Ts&&... args) : super(std::forward<Ts>(args)...) { }
void dequeue(behavior& bhvr) override {
if (bhvr.timeout().valid()) {
auto abs_time = m_scheduling_policy.init_timeout(this, bhvr.timeout());
auto tout =
this->m_scheduling_policy.init_timeout(this, bhvr.timeout());
auto done = false;
while (!done) {
if (!m_resume_policy.await_data(this, abs_time)) {
if (!this->m_resume_policy.await_data(this, tout)) {
bhvr.handle_timeout();
done = true;
}
else {
auto msg = m_priority_policy.next_message(this);
} else {
auto msg = this->m_priority_policy.next_message(this);
// must not return nullptr, because await_data guarantees
// at least one message in our mailbox
CPPA_REQUIRE(msg != nullptr);
done = m_invoke_policy.invoke(this, bhvr, msg);
done = this->m_invoke_policy.invoke(this, msg, bhvr);
}
}
}
else {
} else {
for (;;) {
auto msg = m_priority_policy.next_message(this);
auto msg = this->m_priority_policy.next_message(this);
while (msg) {
if (m_invoke_policy.invoke(this, bhvr, msg)) {
if (this->m_invoke_policy.invoke(this, msg, bhvr)) {
// we're done
return;
}
msg = m_priority_policy.next_message(this);
msg = this->m_priority_policy.next_message(this);
}
m_resume_policy.await_data(this);
this->m_resume_policy.await_data(this);
}
}
}
mailbox_element* dequeue() override {
auto e = try_dequeue();
if (!e) {
this->m_resume_policy.await_data(this);
return try_dequeue(); // guaranteed to succeed after await_data
}
return e;
}
mailbox_element* try_dequeue() override {
return this->m_priority_policy.next_message(this);
}
mailbox_element* try_dequeue(const typename Base::timeout_type& tout) override {
if (this->m_resume_policy.await_data(this, tout)) {
return try_dequeue();
}
return nullptr;
}
};
} } // namespace cppa::detail
} // namespace detail
} // namespace cppa
#endif // PROPER_ACTOR_HPP
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_RAW_ACCESS_HPP
......
......@@ -33,10 +33,10 @@
#include <thread>
#include "cppa/resumable.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/context_switching_actor.hpp"
#include "cppa/util/producer_consumer_list.hpp"
#include "cppa/detail/scheduled_actor_dummy.hpp"
namespace cppa { namespace detail {
......@@ -46,8 +46,9 @@ class thread_pool_scheduler : public scheduler {
public:
using scheduler::init_callback;
using scheduler::actor_fun;
struct dummy : resumable {
resume_result resume(util::fiber*) override;
};
struct worker;
......@@ -59,24 +60,20 @@ class thread_pool_scheduler : public scheduler {
void destroy();
void enqueue(scheduled_actor* what);
local_actor_ptr exec(spawn_options opts, scheduled_actor_ptr ptr) override;
local_actor_ptr exec(spawn_options opts, init_callback init_cb, actor_fun f) override;
void enqueue(resumable* what) override;
private:
//typedef util::single_reader_queue<abstract_scheduled_actor> job_queue;
typedef util::producer_consumer_list<scheduled_actor> job_queue;
typedef util::producer_consumer_list<resumable> job_queue;
size_t m_num_threads;
job_queue m_queue;
scheduled_actor_dummy m_dummy;
dummy m_dummy;
std::thread m_supervisor;
static void worker_loop(worker*);
static void supervisor_loop(job_queue*, scheduled_actor*, size_t);
static void supervisor_loop(job_queue*, resumable*, size_t);
};
......
......@@ -51,8 +51,8 @@ struct extend_helper<D, B, M, Ms...> : extend_helper<D, M<B, D>, Ms...> { };
/**
* @brief Allows convenient definition of types using mixins.
* For example, @p extend<ar, T>::with<ob, fo> is an alias for
* @p fo<ob<ar, T>, T>.
* For example, "extend<ar, T>::with<ob, fo>" is an alias for
* "fo<ob<ar, T>, T>".
*
* Mixins in libcppa always have two template parameters: base type and
* derived type. This allows mixins to make use of the curiously recurring
......@@ -64,7 +64,7 @@ struct extend {
/**
* @brief Identifies the combined type.
*/
template<template<class, class> class... Mixins>
template<CPPA_MIXIN... Mixins>
using with = typename detail::extend_helper<Derived, Base, Mixins...>::type;
};
......
......@@ -33,9 +33,9 @@
#include <map>
#include "cppa/stackless.hpp"
#include "cppa/threadless.hpp"
#include "cppa/extend.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/behavior_stack_based.hpp"
#include "cppa/util/buffer.hpp"
......@@ -45,7 +45,10 @@
#include "cppa/io/accept_handle.hpp"
#include "cppa/io/connection_handle.hpp"
namespace cppa { namespace io {
#include "cppa/policy/sequential_invoke.hpp"
namespace cppa {
namespace io {
class broker;
......@@ -58,7 +61,9 @@ local_actor_ptr init_and_launch(broker_ptr);
* and other components in the network.
* @extends local_actor
*/
class broker : public extend<local_actor>::with<threadless, stackless> {
class broker : public extend<local_actor>::with<behavior_stack_based> {
friend class policy::sequential_invoke;
typedef combined_type super;
......@@ -97,6 +102,7 @@ class broker : public extend<local_actor>::with<threadless, stackless> {
void write(const connection_handle& hdl, util::buffer&& buf);
/*
template<typename F, typename... Ts>
static broker_ptr from(F fun,
input_stream_ptr in,
......@@ -121,6 +127,7 @@ class broker : public extend<local_actor>::with<threadless, stackless> {
std::forward<Ts>(args)...),
std::move(in));
}
*/
template<typename F, typename... Ts>
actor fork(F fun, connection_handle hdl, Ts&&... args) {
......@@ -154,14 +161,16 @@ class broker : public extend<local_actor>::with<threadless, stackless> {
explicit broker(scribe_pointer);
virtual behavior make_behavior() = 0;
private:
actor_addr fork_impl(std::function<void (broker*)> fun,
connection_handle hdl);
actor fork_impl(std::function<void (broker*)> fun,
connection_handle hdl);
static broker_ptr from_impl(std::function<void (broker*)> fun,
input_stream_ptr in,
output_stream_ptr out);
//static broker_ptr from_impl(std::function<void (broker*)> fun,
// input_stream_ptr in,
// output_stream_ptr out);
void invoke_message(const message_header& hdr, any_tuple msg);
......@@ -178,10 +187,27 @@ class broker : public extend<local_actor>::with<threadless, stackless> {
std::map<accept_handle, doorman_pointer> m_accept;
std::map<connection_handle, scribe_pointer> m_io;
policy::sequential_invoke m_invoke;
};
//typedef intrusive_ptr<broker> broker_ptr;
class default_broker : public broker {
public:
typedef std::function<void (broker*)> function_type;
default_broker(input_stream_ptr in, output_stream_ptr out, function_type f);
behavior make_behavior() override;
private:
function_type m_fun;
};
} } // namespace cppa::network
} // namespace io
} // namespace cppa
#endif // CPPA_BROKER_HPP
......@@ -31,6 +31,7 @@
#ifndef CPPA_CONTEXT_HPP
#define CPPA_CONTEXT_HPP
#include <atomic>
#include <cstdint>
#include <functional>
......@@ -42,9 +43,10 @@
#include "cppa/any_tuple.hpp"
#include "cppa/message_id.hpp"
#include "cppa/match_expr.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/typed_actor.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/spawn_options.hpp"
#include "cppa/memory_cached.hpp"
#include "cppa/message_future.hpp"
#include "cppa/message_header.hpp"
......@@ -58,6 +60,8 @@
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa {
// forward declarations
......@@ -66,22 +70,69 @@ class message_future;
class local_scheduler;
class sync_handle_helper;
namespace detail { class receive_policy; }
namespace util {
struct fiber;
} // namespace util
template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
actor spawn(Ts&&... args);
template<spawn_options Options = no_spawn_options, typename... Ts>
actor spawn(Ts&&... args);
/**
* @brief Base class for local running Actors.
* @extends actor
*/
class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_based> {
friend class detail::receive_policy;
class local_actor : public extend<abstract_actor>::with<memory_cached> {
typedef combined_type super;
public:
typedef detail::disposer del;
typedef intrusive::single_reader_queue<mailbox_element, del> mailbox_type;
~local_actor();
inline actor eval_opts(spawn_options opts, actor res) {
if (has_monitor_flag(opts)) {
monitor(res);
}
if (has_link_flag(opts)) {
link_to(res);
}
return res;
}
template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
actor spawn(Ts&&... args) {
auto res = cppa::spawn<Impl, make_unbound(Options)>(std::forward<Ts>(args)...);
return eval_opts(Options, std::move(res));
}
template<spawn_options Options = no_spawn_options, typename... Ts>
actor spawn(Ts&&... args) {
auto res = cppa::spawn<make_unbound(Options)>(std::forward<Ts>(args)...);
return eval_opts(Options, std::move(res));
}
/**
* @brief Sends @p what to the receiver specified in @p dest.
*/
void send_tuple(message_priority prio, const channel& dest, any_tuple what);
/**
* @brief Sends <tt>{what...}</tt> to the receiver specified in @p hdr.
* @pre <tt>sizeof...(Ts) > 0</tt>
*/
template<typename... Ts>
void send(message_priority prio, const channel& whom, Ts&&... what) {
send_tuple(prio, whom, make_any_tuple(std::forward<Ts>(what)...));
}
/**
* @brief Sends @p what to the receiver specified in @p dest.
*/
......@@ -96,6 +147,8 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
send_tuple(whom, make_any_tuple(std::forward<Ts>(what)...));
}
void send_exit(const actor_addr& whom, std::uint32_t reason);
/**
* @brief Sends @p what as a synchronous message to @p whom.
* @param whom Receiver of the message.
......@@ -128,8 +181,8 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
}
template<typename... Ts>
message_future timed_sync_send(const util::duration& rtime,
const actor& dest,
message_future timed_sync_send(const actor& dest,
const util::duration& rtime,
Ts&&... what) {
static_assert(sizeof...(Ts) > 0, "no message to send");
return timed_sync_send_tuple(rtime, dest, make_any_tuple(std::forward<Ts>(what)...));
......@@ -146,6 +199,12 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
const util::duration& rtime,
any_tuple data);
template<typename... Ts>
void delayed_send(const channel& dest, const util::duration& rtime,
Ts&&... args) {
delayed_send_tuple(dest, rtime, make_any_tuple(std::forward<Ts>(args)...));
}
/**
* @brief Causes this actor to subscribe to the group @p what.
*
......@@ -234,15 +293,6 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
demonitor(whom->address());
}
/**
* @brief Can be overridden to initialize an actor before any
* message is handled.
* @warning Must not call blocking functions such as
* {@link cppa::receive receive}.
* @note Calling {@link become} to set an initial behavior is supported.
*/
virtual void init();
/**
* @brief Can be overridden to perform cleanup code after an actor
* finished execution.
......@@ -309,9 +359,7 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
else quit(exit_reason::unhandled_sync_failure);
}
local_actor(bool is_scheduled = false);
virtual bool initialized() const = 0;
local_actor();
inline bool chaining_enabled();
......@@ -332,20 +380,32 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
inline void mark_arrived(message_id response_id);
//virtual void become_waiting_for(behavior, message_id) = 0;
//inline detail::behavior_stack& bhvr_stack();
inline std::uint32_t planned_exit_reason() const;
//virtual void do_become(behavior&& bhvr, bool discard_old) = 0;
inline void planned_exit_reason(std::uint32_t value);
//inline void do_become(const behavior& bhvr, bool discard_old);
actor_state cas_state(actor_state expected, actor_state desired) {
auto e = expected;
do { if (m_state.compare_exchange_weak(e, desired)) return desired; }
while (e == expected);
return e;
}
inline std::uint32_t planned_exit_reason() const;
inline void set_state(actor_state new_value) {
m_state.store(new_value);
}
inline void planned_exit_reason(std::uint32_t value);
inline actor_state state() const {
return m_state;
}
protected:
template<typename... Ts>
inline mailbox_element* new_mailbox_element(Ts&&... args) {
return mailbox_element::create(std::forward<Ts>(args)...);
}
void remove_handler(message_id id);
void cleanup(std::uint32_t reason);
......@@ -353,9 +413,6 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
// true if this actor receives EXIT messages as ordinary messages
bool m_trap_exit;
// true if this actor takes part in cooperative scheduling
bool m_is_scheduled;
// identifies the ID of the last sent synchronous request
message_id m_last_request_id;
......@@ -372,12 +429,12 @@ class local_actor : public extend<abstract_actor>::with<memory_cached, mailbox_b
// {group => subscription} map of all joined groups
std::map<group_ptr, group::subscription> m_subscriptions;
// allows actors to keep previous behaviors and enables unbecome()
//detail::behavior_stack m_bhvr_stack;
// set by quit
std::uint32_t m_planned_exit_reason;
// the state of the (possibly cooperatively scheduled) actor
std::atomic<actor_state> m_state;
/** @endcond */
private:
......
......@@ -53,6 +53,11 @@ class mailbox_based : public Base {
}
}
template<typename... Ts>
inline mailbox_element* new_mailbox_element(Ts&&... args) {
return mailbox_element::create(std::forward<Ts>(args)...);
}
protected:
typedef mailbox_based combined_type;
......@@ -68,11 +73,6 @@ class mailbox_based : public Base {
Base::cleanup(reason);
}
template<typename... Ts>
inline mailbox_element* new_mailbox_element(Ts&&... args) {
return mailbox_element::create(std::forward<Ts>(args)...);
}
mailbox_type m_mailbox;
};
......
......@@ -64,7 +64,7 @@ template<typename T1, typename T2>
inline const T2& deduce_const(const T1&, T2& rhs) { return rhs; }
template<class FilteredPattern>
struct invoke_policy_base {
struct invoke_util_base {
typedef FilteredPattern filtered_pattern;
typedef typename pseudo_tuple_from_type_list<filtered_pattern>::type
tuple_type;
......@@ -72,9 +72,9 @@ struct invoke_policy_base {
// covers wildcard_position::multiple and wildcard_position::in_between
template<wildcard_position, class Pattern, class FilteredPattern>
struct invoke_policy_impl : invoke_policy_base<FilteredPattern> {
struct invoke_util_impl : invoke_util_base<FilteredPattern> {
typedef invoke_policy_base<FilteredPattern> super;
typedef invoke_util_base<FilteredPattern> super;
template<class Tuple>
static bool can_invoke(const std::type_info& type_token,
......@@ -113,12 +113,12 @@ struct invoke_policy_impl : invoke_policy_base<FilteredPattern> {
};
template<>
struct invoke_policy_impl<wildcard_position::nil,
struct invoke_util_impl<wildcard_position::nil,
util::empty_type_list,
util::empty_type_list >
: invoke_policy_base<util::empty_type_list> {
: invoke_util_base<util::empty_type_list> {
typedef invoke_policy_base<util::empty_type_list> super;
typedef invoke_util_base<util::empty_type_list> super;
template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type&,
......@@ -137,12 +137,12 @@ struct invoke_policy_impl<wildcard_position::nil,
};
template<class Pattern, typename... Ts>
struct invoke_policy_impl<wildcard_position::nil,
struct invoke_util_impl<wildcard_position::nil,
Pattern,
util::type_list<Ts...>>
: invoke_policy_base<util::type_list<Ts...>> {
: invoke_util_base<util::type_list<Ts...>> {
typedef invoke_policy_base<util::type_list<Ts...>> super;
typedef invoke_util_base<util::type_list<Ts...>> super;
typedef typename super::tuple_type tuple_type;
......@@ -242,12 +242,12 @@ struct invoke_policy_impl<wildcard_position::nil,
};
template<>
struct invoke_policy_impl<wildcard_position::leading,
struct invoke_util_impl<wildcard_position::leading,
util::type_list<anything>,
util::empty_type_list>
: invoke_policy_base<util::empty_type_list> {
: invoke_util_base<util::empty_type_list> {
typedef invoke_policy_base<util::empty_type_list> super;
typedef invoke_util_base<util::empty_type_list> super;
template<class Tuple>
static inline bool can_invoke(const std::type_info&, const Tuple&) {
......@@ -266,12 +266,12 @@ struct invoke_policy_impl<wildcard_position::leading,
};
template<class Pattern, typename... Ts>
struct invoke_policy_impl<wildcard_position::trailing,
struct invoke_util_impl<wildcard_position::trailing,
Pattern,
util::type_list<Ts...>>
: invoke_policy_base<util::type_list<Ts...>> {
: invoke_util_base<util::type_list<Ts...>> {
typedef invoke_policy_base<util::type_list<Ts...>> super;
typedef invoke_util_base<util::type_list<Ts...>> super;
template<class Tuple>
static bool can_invoke(const std::type_info& arg_types,
......@@ -308,12 +308,12 @@ struct invoke_policy_impl<wildcard_position::trailing,
};
template<class Pattern, typename... Ts>
struct invoke_policy_impl<wildcard_position::leading,
struct invoke_util_impl<wildcard_position::leading,
Pattern,
util::type_list<Ts...>>
: invoke_policy_base<util::type_list<Ts...>> {
: invoke_util_base<util::type_list<Ts...>> {
typedef invoke_policy_base<util::type_list<Ts...>> super;
typedef invoke_util_base<util::type_list<Ts...>> super;
template<class Tuple>
static bool can_invoke(const std::type_info& arg_types,
......@@ -352,8 +352,8 @@ struct invoke_policy_impl<wildcard_position::leading,
};
template<class Pattern>
struct invoke_policy
: invoke_policy_impl<
struct invoke_util
: invoke_util_impl<
get_wildcard_position<Pattern>(),
Pattern,
typename util::tl_filter_not_type<Pattern, anything>::type> {
......@@ -507,7 +507,7 @@ Result unroll_expr(PPFPs& fs,
auto& f = get<N>(fs);
typedef typename util::rm_const_and_ref<decltype(f)>::type Fun;
typedef typename Fun::pattern_type pattern_type;
typedef detail::invoke_policy<pattern_type> policy;
typedef detail::invoke_util<pattern_type> policy;
typename policy::tuple_type targs;
if (policy::prepare_invoke(targs, type_token, is_dynamic, ptr, tup)) {
auto is = util::get_indices(targs);
......@@ -536,7 +536,7 @@ inline bool can_unroll_expr(PPFPs& fs,
auto& f = get<N>(fs);
typedef typename util::rm_const_and_ref<decltype(f)>::type Fun;
typedef typename Fun::pattern_type pattern_type;
typedef detail::invoke_policy<pattern_type> policy;
typedef detail::invoke_util<pattern_type> policy;
return policy::can_invoke(arg_types, tup);
}
......@@ -556,7 +556,7 @@ inline std::uint64_t calc_bitmask(PPFPs& fs,
auto& f = get<N>(fs);
typedef typename util::rm_const_and_ref<decltype(f)>::type Fun;
typedef typename Fun::pattern_type pattern_type;
typedef detail::invoke_policy<pattern_type> policy;
typedef detail::invoke_util<pattern_type> policy;
std::uint64_t result = policy::can_invoke(tinf, tup) ? (0x01 << N) : 0x00;
return result | calc_bitmask(fs, long_constant<N-1l>(), tinf, tup);
}
......
......@@ -34,7 +34,9 @@
#include <cstdint>
#include <type_traits>
#include "cppa/on.hpp"
#include "cppa/match_expr.hpp"
#include "cppa/message_id.hpp"
#include "cppa/partial_function.hpp"
namespace cppa {
......@@ -56,8 +58,10 @@ class continue_helper {
inline continue_helper(message_id mid) : m_mid(mid) { }
template<typename F>
continue_helper& continue_with(F fun);
continue_helper& continue_with(F) {
}
inline message_id get_message_id() const {
return m_mid;
}
......@@ -78,12 +82,14 @@ class message_future {
message_future() = delete;
inline continue_helper then(const partial_function& pfun) {
inline continue_helper then(const behavior&) {
//FIXME
return message_id{};
}
inline continue_helper await(const partial_function& pfun) {
inline continue_helper await(const behavior&) {
//FIXME
return message_id{};
}
/**
......@@ -113,7 +119,7 @@ class message_future {
continue_helper
>::type
then(Fs... fs) {
return then(fs2bhvr(std::move(fs)...));
return then(partial_function{(on_arg_match >> std::move(fs))...});
}
/**
......@@ -124,7 +130,7 @@ class message_future {
template<typename... Fs>
typename std::enable_if<util::all_callable<Fs...>::value>::type
await(Fs... fs) {
await(fs2bhvr({std::move(fs)...}));
await(partial_function{(on_arg_match >> std::move(fs))...});
}
/**
......@@ -142,8 +148,6 @@ class message_future {
message_id m_mid;
local_actor* self;
partial_function fs2bhvr(partial_function pf);
inline void check_consistency() { }
};
......@@ -193,17 +197,6 @@ inline sync_handle_helper handle_response(const message_future& f) {
return {f};
}
/**
* @brief Handles a synchronous response message in an event-based way.
* @param handle A future for a synchronous response.
* @throws std::logic_error if @p handle is not valid or if the actor
* already received the response for @p handle
* @relates message_future
*/
inline sync_receive_helper receive_response(const message_future& f) {
return {f};
}
} // namespace cppa
#endif // CPPA_MESSAGE_FUTURE_HPP
......@@ -28,7 +28,6 @@
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_OPENCL_ACTOR_FACADE_HPP
#define CPPA_OPENCL_ACTOR_FACADE_HPP
......@@ -55,34 +54,35 @@
#include "cppa/detail/scheduled_actor_dummy.hpp"
namespace cppa { namespace opencl {
namespace cppa {
namespace opencl {
class opencl_metainfo;
template<typename Signature>
template <typename Signature>
class actor_facade;
template<typename Ret, typename... Args>
class actor_facade<Ret(Args...)> : public actor {
template <typename Ret, typename... Args>
class actor_facade<Ret(Args...)> : public abstract_actor {
friend class command<actor_facade, Ret>;
public:
typedef cow_tuple<typename util::rm_const_and_ref<Args>::type...> args_tuple;
typedef cow_tuple<typename util::rm_const_and_ref<Args>::type...>
args_tuple;
typedef std::function<optional<args_tuple>(any_tuple)> arg_mapping;
typedef std::function<any_tuple(Ret&)> result_mapping;
static intrusive_ptr<actor_facade> create(const program& prog,
const char* kernel_name,
arg_mapping map_args,
result_mapping map_result,
const dim_vec& global_dims,
const dim_vec& offsets,
const dim_vec& local_dims) {
static intrusive_ptr<actor_facade>
create(const program& prog, const char* kernel_name, arg_mapping map_args,
result_mapping map_result, const dim_vec& global_dims,
const dim_vec& offsets, const dim_vec& local_dims) {
if (global_dims.empty()) {
auto str = "OpenCL kernel needs at least 1 global dimension.";
CPPA_LOGM_ERROR(detail::demangle(typeid(actor_facade)).c_str(), str);
CPPA_LOGM_ERROR(detail::demangle(typeid(actor_facade)).c_str(),
str);
throw std::runtime_error(str);
}
auto check_vec = [&](const dim_vec& vec, const char* name) {
......@@ -90,30 +90,27 @@ class actor_facade<Ret(Args...)> : public actor {
std::ostringstream oss;
oss << name << " vector is not empty, but "
<< "its size differs from global dimensions vector's size";
CPPA_LOGM_ERROR(detail::demangle<actor_facade>().c_str(), oss.str());
CPPA_LOGM_ERROR(detail::demangle<actor_facade>().c_str(),
oss.str());
throw std::runtime_error(oss.str());
}
};
check_vec(offsets, "offsets");
check_vec(local_dims, "local dimensions");
cl_int err{0};
cl_int err{ 0 };
kernel_ptr kernel;
kernel.adopt(clCreateKernel(prog.m_program.get(),
kernel_name,
&err));
kernel.adopt(clCreateKernel(prog.m_program.get(), kernel_name, &err));
if (err != CL_SUCCESS) {
std::ostringstream oss;
oss << "clCreateKernel: " << get_opencl_error(err);
CPPA_LOGM_ERROR(detail::demangle<actor_facade>().c_str(), oss.str());
CPPA_LOGM_ERROR(detail::demangle<actor_facade>().c_str(),
oss.str());
throw std::runtime_error(oss.str());
}
return new actor_facade<Ret (Args...)>{prog,
kernel,
global_dims,
offsets,
local_dims,
std::move(map_args),
std::move(map_result)};
return new actor_facade<Ret(Args...)>{
prog, kernel, global_dims, offsets,
local_dims, std::move(map_args), std::move(map_result)
};
}
void enqueue(const message_header& hdr, any_tuple msg) override {
......@@ -124,47 +121,38 @@ class actor_facade<Ret(Args...)> : public actor {
private:
actor_facade(const program& prog,
kernel_ptr kernel,
actor_facade(const program& prog, kernel_ptr kernel,
const dim_vec& global_dimensions,
const dim_vec& global_offsets,
const dim_vec& local_dimensions,
arg_mapping map_args,
result_mapping map_result)
: m_kernel(kernel)
, m_program(prog.m_program)
, m_context(prog.m_context)
, m_queue(prog.m_queue)
, m_global_dimensions(global_dimensions)
, m_global_offsets(global_offsets)
, m_local_dimensions(local_dimensions)
, m_map_args(std::move(map_args))
, m_map_result(std::move(map_result))
{
const dim_vec& global_offsets, const dim_vec& local_dimensions,
arg_mapping map_args, result_mapping map_result)
: m_kernel(kernel), m_program(prog.m_program),
m_context(prog.m_context), m_queue(prog.m_queue),
m_global_dimensions(global_dimensions),
m_global_offsets(global_offsets),
m_local_dimensions(local_dimensions), m_map_args(std::move(map_args)),
m_map_result(std::move(map_result)) {
CPPA_LOG_TRACE("id: " << this->id());
}
template<long... Is>
void enqueue_impl(const actor_ptr& sender,
any_tuple msg,
message_id id,
template <long... Is>
void enqueue_impl(const actor_ptr& sender, any_tuple msg, message_id id,
util::int_list<Is...>) {
auto opt = m_map_args(std::move(msg));
if (opt) {
response_handle handle{this, sender, id.response_id()};
response_handle handle{ this, sender, id.response_id() };
size_t ret_size = std::accumulate(m_global_dimensions.begin(),
m_global_dimensions.end(),
1, std::multiplies<size_t>{});
m_global_dimensions.end(), 1,
std::multiplies<size_t>{});
std::vector<mem_ptr> arguments;
add_arguments_to_kernel<Ret>(arguments,
ret_size,
add_arguments_to_kernel<Ret>(arguments, ret_size,
get_ref<Is>(*opt)...);
auto cmd = make_counted<command<actor_facade, Ret>>(handle,
this,
std::move(arguments));
auto cmd = make_counted<command<actor_facade, Ret>>(
handle, this, std::move(arguments));
cmd->enqueue();
} else {
CPPA_LOGMF(CPPA_ERROR, this,
"actor_facade::enqueue() tuple_cast failed.");
}
else { CPPA_LOGMF(CPPA_ERROR, this, "actor_facade::enqueue() tuple_cast failed."); }
}
typedef std::vector<mem_ptr> args_vec;
......@@ -180,36 +168,30 @@ class actor_facade<Ret(Args...)> : public actor {
result_mapping m_map_result;
void add_arguments_to_kernel_rec(args_vec& arguments) {
cl_int err{0};
for(size_t i = 1; i < arguments.size(); ++i) {
err = clSetKernelArg(m_kernel.get(),
(i-1),
sizeof(cl_mem),
cl_int err{ 0 };
for (size_t i = 1; i < arguments.size(); ++i) {
err = clSetKernelArg(m_kernel.get(), (i - 1), sizeof(cl_mem),
static_cast<void*>(&arguments[i]));
CPPA_LOG_ERROR_IF(err != CL_SUCCESS,
"clSetKernelArg: " << get_opencl_error(err));
}
err = clSetKernelArg(m_kernel.get(),
arguments.size()-1,
sizeof(cl_mem),
static_cast<void*>(&arguments[0]));
err = clSetKernelArg(m_kernel.get(), arguments.size() - 1,
sizeof(cl_mem), static_cast<void*>(&arguments[0]));
CPPA_LOG_ERROR_IF(err != CL_SUCCESS,
"clSetKernelArg: " << get_opencl_error(err));
}
template<typename T0, typename... Ts>
void add_arguments_to_kernel_rec(args_vec& arguments,
T0& arg0, Ts&... args) {
cl_int err{0};
auto buf = clCreateBuffer(m_context.get(),
CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(typename T0::value_type) * arg0.size(),
arg0.data(),
&err);
template <typename T0, typename... Ts>
void add_arguments_to_kernel_rec(args_vec& arguments, T0& arg0,
Ts&... args) {
cl_int err{ 0 };
auto buf = clCreateBuffer(
m_context.get(), CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(typename T0::value_type) * arg0.size(), arg0.data(), &err);
if (err != CL_SUCCESS) {
CPPA_LOGMF(CPPA_ERROR, this, "clCreateBuffer: " << get_opencl_error(err));
}
else {
CPPA_LOGMF(CPPA_ERROR, this,
"clCreateBuffer: " << get_opencl_error(err));
} else {
mem_ptr tmp;
tmp.adopt(std::move(buf));
arguments.push_back(tmp);
......@@ -217,21 +199,18 @@ class actor_facade<Ret(Args...)> : public actor {
}
}
template<typename R, typename... Ts>
void add_arguments_to_kernel(args_vec& arguments,
size_t ret_size,
template <typename R, typename... Ts>
void add_arguments_to_kernel(args_vec& arguments, size_t ret_size,
Ts&&... args) {
arguments.clear();
cl_int err{0};
auto buf = clCreateBuffer(m_context.get(),
CL_MEM_WRITE_ONLY,
cl_int err{ 0 };
auto buf = clCreateBuffer(m_context.get(), CL_MEM_WRITE_ONLY,
sizeof(typename R::value_type) * ret_size,
nullptr,
&err);
nullptr, &err);
if (err != CL_SUCCESS) {
CPPA_LOGMF(CPPA_ERROR, this, "clCreateBuffer: " << get_opencl_error(err));
}
else {
CPPA_LOGMF(CPPA_ERROR, this,
"clCreateBuffer: " << get_opencl_error(err));
} else {
mem_ptr tmp;
tmp.adopt(std::move(buf));
arguments.push_back(tmp);
......@@ -241,6 +220,7 @@ class actor_facade<Ret(Args...)> : public actor {
};
} } // namespace cppa::opencl
} // namespace opencl
} // namespace cppa
#endif // CPPA_OPENCL_ACTOR_FACADE_HPP
......@@ -33,10 +33,14 @@
#include "cppa/policy/no_resume.hpp"
#include "cppa/policy/prioritizing.hpp"
#include "cppa/policy/no_scheduling.hpp"
#include "cppa/policy/invoke_policy.hpp"
#include "cppa/policy/nestable_invoke.hpp"
#include "cppa/policy/not_prioritizing.hpp"
#include "cppa/policy/sequential_invoke.hpp"
#include "cppa/policy/event_based_resume.hpp"
#include "cppa/policy/invoke_policy_base.hpp"
#include "cppa/policy/middleman_scheduling.hpp"
#include "cppa/policy/cooperative_scheduling.hpp"
#include "cppa/policy/context_switching_resume.hpp"
#endif // CPPA_POLICY_HPP
......@@ -27,19 +27,26 @@
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_CONTEXT_SWITCHING_ACTOR_HPP
#define CPPA_CONTEXT_SWITCHING_ACTOR_HPP
#include "cppa/config.hpp"
#include "cppa/resumable.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/mailbox_element.hpp"
#include "cppa/util/fiber.hpp"
#include "cppa/policy/resume_policy.hpp"
#include "cppa/detail/yield_interface.hpp"
namespace cppa { class local_actor; }
namespace cppa {
class local_actor;
}
namespace cppa { namespace policy {
namespace cppa {
namespace policy {
/**
* @brief Context-switching actor implementation.
......@@ -49,9 +56,57 @@ class context_switching_resume {
public:
// required by util::fiber
static void trampoline(void* _this);
// Base must be a mailbox-based actor
template<class Base>
struct mixin : Base, resumable {
template<typename... Ts>
mixin(Ts&&... args)
: Base(std::forward<Ts>(args)...)
, m_fiber(context_switching_resume::trampoline,
static_cast<blocking_untyped_actor*>(this)) { }
resumable::resume_result resume(util::fiber* from) override {
CPPA_REQUIRE(from != nullptr);
using namespace detail;
for (;;) {
switch (call(&m_fiber, from)) {
case yield_state::done: {
CPPA_REQUIRE(next_job == nullptr);
return resumable::done;
}
case yield_state::ready: { break; }
case yield_state::blocked: {
CPPA_REQUIRE(next_job == nullptr);
CPPA_REQUIRE(m_chained_actor == nullptr);
switch (this->cas_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready: {
// restore variables
CPPA_REQUIRE(next_job == nullptr);
break;
}
case actor_state::blocked: {
// wait until someone re-schedules that actor
return resumable::resume_later;
}
default: { CPPA_CRITICAL("illegal yield result"); }
}
break;
}
default: { CPPA_CRITICAL("illegal state"); }
}
}
}
util::fiber m_fiber;
};
template<class Actor, typename F>
template <class Actor, typename F>
void fetch_messages(Actor* self, F cb) {
auto e = self->m_mailbox.try_pop();
while (e == nullptr) {
......@@ -63,7 +118,8 @@ class context_switching_resume {
self->set_state(actor_state::ready);
}
// wait until actor becomes rescheduled
else detail::yield(detail::yield_state::blocked);
else
detail::yield(detail::yield_state::blocked);
}
}
// ok, we have at least one message
......@@ -73,7 +129,7 @@ class context_switching_resume {
}
}
template<class Actor, typename F>
template <class Actor, typename F>
void try_fetch_messages(Actor* self, F cb) {
auto e = self->m_mailbox.try_pop();
while (e) {
......@@ -83,57 +139,33 @@ class context_switching_resume {
}
template<class Actor>
resume_result resume(Actor* self, util::fiber* from) {
CPPA_LOG_TRACE("state = " << static_cast<int>(self->state()));
CPPA_REQUIRE(from != nullptr);
CPPA_REQUIRE(next_job == nullptr);
using namespace detail;
for (;;) {
switch (call(&m_fiber, from)) {
case yield_state::done: {
CPPA_REQUIRE(next_job == nullptr);
return resume_result::actor_done;
}
case yield_state::ready: {
break;
}
case yield_state::blocked: {
CPPA_REQUIRE(next_job == nullptr);
CPPA_REQUIRE(m_chained_actor == nullptr);
switch (compare_exchange_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready: {
// restore variables
CPPA_REQUIRE(next_job == nullptr);
break;
}
case actor_state::blocked: {
// wait until someone re-schedules that actor
return resume_result::actor_blocked;
}
default: {
CPPA_CRITICAL("illegal yield result");
}
}
break;
}
default: {
CPPA_CRITICAL("illegal state");
}
void await_data(Actor* self) {
while (self->m_mailbox.can_fetch_more() == false) {
self->set_state(actor_state::about_to_block);
// make sure mailbox is empty
if (self->m_mailbox.can_fetch_more()) {
// someone preempt us => continue
self->set_state(actor_state::ready);
}
// wait until actor becomes rescheduled
else detail::yield(detail::yield_state::blocked);
}
}
private:
template<class Actor, typename AbsTimeout>
bool await_data(Actor* self, const AbsTimeout&) {
await_data(self);
return true;
}
// required by util::fiber
static void trampoline(void* _this);
private:
// members
util::fiber m_fiber;
};
} } // namespace cppa::policy
} // namespace policy
} // namespace cppa
#endif // CPPA_CONTEXT_SWITCHING_ACTOR_HPP
......@@ -31,6 +31,17 @@
#ifndef CPPA_COOPERATIVE_SCHEDULING_HPP
#define CPPA_COOPERATIVE_SCHEDULING_HPP
#include <atomic>
#include "cppa/any_tuple.hpp"
#include "cppa/resumable.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/message_header.hpp"
#include "cppa/detail/yield_interface.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa { namespace policy {
class cooperative_scheduling {
......@@ -49,12 +60,12 @@ class cooperative_scheduling {
// this does return nullptr
template<class Actor, typename F>
void fetch_messages(Actor* self, F cb) {
auto e = self->m_mailbox.try_pop();
auto e = self->mailbox().try_pop();
while (e == nullptr) {
if (self->m_mailbox.can_fetch_more() == false) {
if (self->mailbox().can_fetch_more() == false) {
self->set_state(actor_state::about_to_block);
// make sure mailbox is empty
if (self->m_mailbox.can_fetch_more()) {
if (self->mailbox().can_fetch_more()) {
// someone preempt us => continue
self->set_state(actor_state::ready);
}
......@@ -65,7 +76,7 @@ class cooperative_scheduling {
// ok, we have at least one message
while (e) {
cb(e);
e = self->m_mailbox.try_pop();
e = self->mailbox().try_pop();
}
}
......@@ -76,6 +87,59 @@ class cooperative_scheduling {
fetch_messages(self, cb);
}
template<class Actor>
inline void launch(Actor*) {
static_cast<void>(m_hidden);
}
template<class Actor>
void enqueue(Actor* self, const message_header& hdr, any_tuple& msg) {
auto e = self->new_mailbox_element(hdr, std::move(msg));
switch (self->mailbox().enqueue(e)) {
case intrusive::first_enqueued: {
auto state = self->state();
auto set_ready = [&]() -> bool {
auto s = self->cas_state(state, actor_state::ready);
return s == actor_state::ready;
};
for (;;) {
switch (state) {
case actor_state::blocked: {
if (set_ready()) {
CPPA_REQUIRE(m_scheduler != nullptr);
//m_scheduler->enqueue(this);
return;
}
break;
}
case actor_state::about_to_block: {
if (set_ready()) {
return;
}
break;
}
default: return;
}
}
break;
}
case intrusive::queue_closed: {
if (hdr.id.is_request()) {
//FIXME
//detail::sync_request_bouncer f{exit_reason()};
//f(hdr.sender, hdr.id);
}
break;
}
default: break;
}
}
private:
// denotes whether this actor is ignored by await_all_actors_done()
bool m_hidden;
};
} } // namespace cppa::policy
......
......@@ -40,163 +40,138 @@
#include "cppa/config.hpp"
#include "cppa/extend.hpp"
#include "cppa/behavior.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/policy/resume_policy.hpp"
namespace cppa { namespace policy {
/**
* @brief Base class for all event-based actor implementations.
* @extends scheduled_actor
*/
class event_based_resume {
public:
template<class Actor>
void await_data(Actor*) {
static_assert(std::is_same<Actor, Actor>::value == false,
"The event_based_resume policy cannot be used "
"to implement blocking actors");
}
template<class Actor>
bool await_data(Actor*, const util::duration&) {
static_assert(std::is_same<Actor, Actor>::value == false,
"The event_based_resume policy cannot be used "
"to implement blocking actors");
}
template<class Actor>
timeout_type init_timeout(Actor* self, const util::duration& rel_time) {
// request explicit timeout message
self->request_timeout(rel_time);
return 0; // return some dummy value
}
template<class Actor, typename F>
void fetch_messages(Actor* self, F cb) {
auto e = self->m_mailbox.try_pop();
while (e) {
cb(e);
e = self->m_mailbox.try_pop();
}
}
template<class Actor, typename F>
inline void fetch_messages(Actor* self, F cb, timeout_type) {
// a call to this call is always preceded by init_timeout,
// which will trigger a timeout message
fetch_messages(self, cb);
}
template<class Actor, typename F>
void try_fetch_messages(Actor* self, F cb) {
// try_fetch and fetch have the same semantics for event-based actors
fetch_messages(self, cb);
}
template<class Actor>
resume_result resume(Actor* self, util::fiber*) {
CPPA_LOG_TRACE("id = " << self->id()
<< ", state = " << static_cast<int>(self->state()));
CPPA_REQUIRE( self->state() == actor_state::ready
|| self->state() == actor_state::pending);
auto done_cb = [&]() -> bool {
CPPA_LOG_TRACE("");
if (self->exit_reason() == self->exit_reason::not_exited) {
if (self->planned_exit_reason() == self->exit_reason::not_exited) {
self->planned_exit_reason(exit_reason::normal);
}
self->on_exit();
if (!m_bhvr_stack.empty()) {
planned_exit_reason(exit_reason::not_exited);
return false; // on_exit did set a new behavior
// Base must be a mailbox-based actor
template<class Base>
struct mixin : Base, resumable {
template<typename... Ts>
mixin(Ts&&... args) : Base(std::forward<Ts>(args)...) { }
resumable::resume_result resume(util::fiber*) override {
CPPA_LOG_TRACE("id = " << this->id()
<< ", state = " << static_cast<int>(this->state()));
CPPA_REQUIRE( this->state() == actor_state::ready
|| this->state() == actor_state::pending);
auto done_cb = [&]() -> bool {
CPPA_LOG_TRACE("");
if (this->exit_reason() == exit_reason::not_exited) {
if (this->planned_exit_reason() == exit_reason::not_exited) {
this->planned_exit_reason(exit_reason::normal);
}
this->on_exit();
if (!this->bhvr_stack().empty()) {
this->planned_exit_reason(exit_reason::not_exited);
return false; // on_exit did set a new behavior
}
this->cleanup(this->planned_exit_reason());
}
cleanup(planned_exit_reason());
}
set_state(actor_state::done);
m_bhvr_stack.clear();
m_bhvr_stack.cleanup();
on_exit();
this->set_state(actor_state::done);
this->bhvr_stack().clear();
this->bhvr_stack().cleanup();
this->on_exit();
return true;
};
CPPA_REQUIRE(next_job == nullptr);
return true;
};
CPPA_REQUIRE(next_job == nullptr);
try {
//auto e = m_mailbox.try_pop();
for (auto e = m_mailbox.try_pop(); ; e = m_mailbox.try_pop()) {
//e = m_mailbox.try_pop();
if (e == nullptr) {
CPPA_REQUIRE(next_job == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "no more element in mailbox; going to block");
set_state(actor_state::about_to_block);
std::atomic_thread_fence(std::memory_order_seq_cst);
if (this->m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready:
// interrupted by arriving message
// restore members
CPPA_REQUIRE(m_chained_actor == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"interrupted by arriving message");
break;
case actor_state::blocked:
CPPA_LOGMF(CPPA_DEBUG, self, "set state successfully to blocked");
// done setting actor to blocked
return resume_result::actor_blocked;
default:
CPPA_LOGMF(CPPA_ERROR, self, "invalid state");
CPPA_CRITICAL("invalid state");
};
try {
//auto e = m_mailbox.try_pop();
for (auto e = this->m_mailbox.try_pop(); ; e = this->m_mailbox.try_pop()) {
//e = m_mailbox.try_pop();
if (e == nullptr) {
CPPA_REQUIRE(next_job == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "no more element in mailbox; going to block");
this->set_state(actor_state::about_to_block);
std::atomic_thread_fence(std::memory_order_seq_cst);
if (this->m_mailbox.can_fetch_more() == false) {
switch (this->cas_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready:
// interrupted by arriving message
// restore members
CPPA_REQUIRE(m_chained_actor == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"interrupted by arriving message");
break;
case actor_state::blocked:
CPPA_LOGMF(CPPA_DEBUG, self, "set state successfully to blocked");
// done setting actor to blocked
return resumable::resume_later;
default:
CPPA_LOGMF(CPPA_ERROR, self, "invalid state");
CPPA_CRITICAL("invalid state");
};
}
else {
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"mailbox can fetch more");
this->set_state(actor_state::ready);
}
}
/*
else {
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"mailbox can fetch more");
set_state(actor_state::ready);
CPPA_REQUIRE(m_chained_actor == nullptr);
}
}
else {
CPPA_LOGMF(CPPA_DEBUG, self, "try to invoke message: " << to_string(e->msg));
if (m_bhvr_stack.invoke(m_recv_policy, this, e)) {
CPPA_LOG_DEBUG_IF(m_chained_actor,
"set actor with ID "
<< m_chained_actor->id()
<< " as successor");
if (m_bhvr_stack.empty() && done_cb()) {
CPPA_LOGMF(CPPA_DEBUG, self, "behavior stack empty");
return resume_result::actor_done;
if (this->bhvr_stack().invoke(m_recv_policy, this, e)) {
CPPA_LOG_DEBUG_IF(m_chained_actor,
"set actor with ID "
<< m_chained_actor->id()
<< " as successor");
if (this->bhvr_stack().empty() && done_cb()) {
CPPA_LOGMF(CPPA_DEBUG, self, "behavior stack empty");
return resume_result::actor_done;
}
this->bhvr_stack().cleanup();
}
m_bhvr_stack.cleanup();
}
*/
}
}
}
catch (actor_exited& what) {
CPPA_LOG_INFO("actor died because of exception: actor_exited, "
"reason = " << what.reason());
if (exit_reason() == exit_reason::not_exited) {
quit(what.reason());
catch (actor_exited& what) {
CPPA_LOG_INFO("actor died because of exception: actor_exited, "
"reason = " << what.reason());
if (this->exit_reason() == exit_reason::not_exited) {
this->quit(what.reason());
}
}
}
catch (std::exception& e) {
CPPA_LOG_WARNING("actor died because of exception: "
<< detail::demangle(typeid(e))
<< ", what() = " << e.what());
if (exit_reason() == exit_reason::not_exited) {
quit(exit_reason::unhandled_exception);
catch (std::exception& e) {
CPPA_LOG_WARNING("actor died because of exception: "
<< detail::demangle(typeid(e))
<< ", what() = " << e.what());
if (this->exit_reason() == exit_reason::not_exited) {
this->quit(exit_reason::unhandled_exception);
}
}
}
catch (...) {
CPPA_LOG_WARNING("actor died because of an unknown exception");
if (exit_reason() == exit_reason::not_exited) {
quit(exit_reason::unhandled_exception);
catch (...) {
CPPA_LOG_WARNING("actor died because of an unknown exception");
if (this->exit_reason() == exit_reason::not_exited) {
this->quit(exit_reason::unhandled_exception);
}
}
done_cb();
return resumable::done;
}
done_cb();
return resume_result::actor_done;
};
template<class Actor>
void await_data(Actor*) {
static_assert(std::is_same<Actor, Actor>::value == false,
"The event-based resume policy cannot be used "
"to implement blocking actors");
}
template<class Actor>
bool await_data(Actor*, const util::duration&) {
static_assert(std::is_same<Actor, Actor>::value == false,
"The event-based resume policy cannot be used "
"to implement blocking actors");
}
};
......
......@@ -28,8 +28,8 @@
\******************************************************************************/
#ifndef CPPA_NESTABLE_RECEIVE_POLICY_HPP
#define CPPA_NESTABLE_RECEIVE_POLICY_HPP
#ifndef CPPA_INVOKE_POLICY_HPP
#define CPPA_INVOKE_POLICY_HPP
#include <list>
#include <memory>
......@@ -45,6 +45,7 @@
#include "cppa/mailbox_element.hpp"
#include "cppa/partial_function.hpp"
#include "cppa/util/dptr.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/matches.hpp"
......@@ -59,35 +60,38 @@ enum receive_policy_flag {
rp_sequential
};
enum handle_message_result {
hm_timeout_msg,
hm_skip_msg,
hm_drop_msg,
hm_cache_msg,
hm_msg_handled
};
template<receive_policy_flag X>
struct rp_flag { typedef std::integral_constant<receive_policy_flag, X> type; };
/**
* @brief The invoke_policy <b>concept</b> class. Please note that this
* class is <b>not</b> implemented. It only explains the all
* required member function and their behavior for any resume policy.
*/
template<class Derived>
class invoke_policy_base {
class invoke_policy {
public:
typedef mailbox_element* pointer;
typedef std::unique_ptr<mailbox_element, detail::disposer> smart_pointer;
enum handle_message_result {
hm_timeout_msg,
hm_skip_msg,
hm_drop_msg,
hm_cache_msg,
hm_msg_handled
};
template<class Client, class Fun>
bool invoke_from_cache(Client* client,
template<class Actor, class Fun>
bool invoke_from_cache(Actor* self,
Fun& fun,
message_id awaited_response = message_id{}) {
std::integral_constant<receive_policy_flag, Client::receive_flag> policy;
auto i = m_cache.begin();
auto e = m_cache.end();
while (i != e) {
switch (this->handle_message(client, i->get(), fun,
awaited_response, policy)) {
switch (this->handle_message(self, i->get(), fun, awaited_response)) {
case hm_msg_handled: {
m_cache.erase(i);
return true;
......@@ -113,15 +117,14 @@ class invoke_policy_base {
m_cache.emplace_back(std::move(node_ptr));
}
template<class Client, class Fun>
bool invoke(Client* client,
template<class Actor, class Fun>
bool invoke(Actor* self,
pointer node_ptr,
Fun& fun,
message_id awaited_response = message_id()) {
smart_pointer node(node_ptr);
std::integral_constant<receive_policy_flag, Client::receive_flag> policy;
switch (this->handle_message(client, node.get(), fun,
awaited_response, policy)) {
switch (this->handle_message(self, node.get(), fun,
awaited_response)) {
case hm_msg_handled: {
return true;
}
......@@ -142,71 +145,71 @@ class invoke_policy_base {
return false;
}
template<class Client, class FunOrBehavior>
inline void receive_wo_timeout(Client *client, FunOrBehavior& fun) {
if (!invoke_from_cache(client, fun)) {
while (!invoke(client, client->await_message(), fun)) { }
template<class Actor, class FunOrBehavior>
inline void receive_wo_timeout(Actor* self, FunOrBehavior& fun) {
if (!invoke_from_cache(self, fun)) {
while (!invoke(self, self->await_message(), fun)) { }
}
}
template<class Client>
void receive(Client* client, partial_function& fun) {
receive_wo_timeout(client, fun);
template<class Actor>
void receive(Actor* self, partial_function& fun) {
receive_wo_timeout(self, fun);
}
template<class Client>
void receive(Client* client, behavior& bhvr) {
template<class Actor>
void receive(Actor* self, behavior& bhvr) {
if (!bhvr.timeout().valid()) {
receive_wo_timeout(client, bhvr);
receive_wo_timeout(self, bhvr);
}
else if (!invoke_from_cache(client, bhvr)) {
else if (!invoke_from_cache(self, bhvr)) {
if (bhvr.timeout().is_zero()) {
pointer e = nullptr;
while ((e = client->try_pop()) != nullptr) {
while ((e = self->try_pop()) != nullptr) {
CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr)) {
if (invoke(self, e, bhvr)) {
return; // done
}
}
handle_timeout(client, bhvr);
dptr()->handle_timeout(self, bhvr);
}
else {
auto timeout = client->init_timeout(bhvr.timeout());
auto timeout = self->init_timeout(bhvr.timeout());
pointer e = nullptr;
while ((e = client->await_message(timeout)) != nullptr) {
while ((e = self->await_message(timeout)) != nullptr) {
CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr)) {
if (invoke(self, e, bhvr)) {
return; // done
}
}
handle_timeout(client, bhvr);
dptr()->handle_timeout(self, bhvr);
}
}
}
template<class Client>
void receive(Client* client, behavior& bhvr, message_id mid) {
template<class Actor>
void receive(Actor* self, behavior& bhvr, message_id mid) {
CPPA_REQUIRE(mid.is_response());
if (!invoke_from_cache(client, bhvr, mid)) {
if (!invoke_from_cache(self, bhvr, mid)) {
if (bhvr.timeout().valid()) {
CPPA_REQUIRE(bhvr.timeout().is_zero() == false);
auto timeout = client->init_timeout(bhvr.timeout());
auto timeout = self->init_timeout(bhvr.timeout());
pointer e = nullptr;
while ((e = client->await_message(timeout)) != nullptr) {
while ((e = self->await_message(timeout)) != nullptr) {
CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr, mid)) {
if (invoke(self, e, bhvr, mid)) {
return; // done
}
}
handle_timeout(client, bhvr);
dptr()->handle_timeout(self, bhvr);
}
else while (!invoke(client, client->await_message(), bhvr, mid)) { }
else while (!invoke(self, self->await_message(), bhvr, mid)) { }
}
}
template<class Client>
mailbox_element* fetch_message(Client* client) {
return client->await_message();
template<class Actor>
mailbox_element* fetch_message(Actor* self) {
return self->await_message();
}
typedef typename rp_flag<rp_nestable>::type nestable;
......@@ -216,13 +219,8 @@ class invoke_policy_base {
std::list<std::unique_ptr<mailbox_element, detail::disposer> > m_cache;
template<class Client>
inline void handle_timeout(Client* client, behavior& bhvr) {
client->handle_timeout(bhvr);
}
template<class Client>
inline void handle_timeout(Client*, partial_function&) {
template<class Actor>
inline void handle_timeout(Actor*, partial_function&) {
CPPA_CRITICAL("handle_timeout(partial_function&)");
}
......@@ -239,11 +237,11 @@ class invoke_policy_base {
// identifies 'special' messages that should not be processed normally:
// - system messages such as EXIT (if client doesn't trap exits) and TIMEOUT
// - system messages such as EXIT (if self doesn't trap exits) and TIMEOUT
// - expired synchronous response messages
template<class Client>
filter_result filter_msg(Client* client, pointer node) {
template<class Actor>
filter_result filter_msg(Actor* self, pointer node) {
const any_tuple& msg = node->msg;
auto mid = node->mid;
auto& arr = detail::static_types_array<atom_value, std::uint32_t>::arr;
......@@ -254,9 +252,9 @@ class invoke_policy_base {
auto v1 = msg.get_as<std::uint32_t>(1);
if (v0 == atom("EXIT")) {
CPPA_REQUIRE(!mid.valid());
if (client->m_trap_exit == false) {
if (self->trap_exit() == false) {
if (v1 != exit_reason::normal) {
client->quit(v1);
self->quit(v1);
return non_normal_exit_signal;
}
return normal_exit_signal;
......@@ -264,8 +262,9 @@ class invoke_policy_base {
}
else if (v0 == atom("SYNC_TOUT")) {
CPPA_REQUIRE(!mid.valid());
return client->waits_for_timeout(v1) ? timeout_message
: expired_timeout_message;
//FIXME
//return self->waits_for_timeout(v1) ? timeout_message
// : expired_timeout_message;
}
}
else if ( msg.size() == 1
......@@ -275,7 +274,7 @@ class invoke_policy_base {
return timeout_response_message;
}
if (mid.is_response()) {
return (client->awaits(mid)) ? sync_response
return (self->awaits(mid)) ? sync_response
: expired_sync_response;
}
return ordinary_message;
......@@ -283,37 +282,37 @@ class invoke_policy_base {
public:
template<class Client>
inline response_handle fetch_response_handle(Client* cl, int) {
template<class Actor>
inline response_handle fetch_response_handle(Actor* cl, int) {
return cl->make_response_handle();
}
template<class Client>
inline response_handle fetch_response_handle(Client*, response_handle& hdl) {
template<class Actor>
inline response_handle fetch_response_handle(Actor*, response_handle& hdl) {
return std::move(hdl);
}
// - extracts response message from handler
// - returns true if fun was successfully invoked
template<class Client, class Fun, class MaybeResponseHandle = int>
optional<any_tuple> invoke_fun(Client* client,
template<class Actor, class Fun, class MaybeResponseHandle = int>
optional<any_tuple> invoke_fun(Actor* self,
any_tuple& msg,
message_id& mid,
Fun& fun,
MaybeResponseHandle hdl = MaybeResponseHandle{}) {
auto res = fun(msg); // might change mid
if (res) {
//message_header hdr{client, sender, mid.is_request() ? mid.response_id()
//message_header hdr{self, sender, mid.is_request() ? mid.response_id()
// : message_id{}};
if (res->empty()) {
// make sure synchronous requests
// always receive a response
if (mid.is_request() && !mid.is_answered()) {
CPPA_LOGMF(CPPA_WARNING, client,
"actor with ID " << client->id()
CPPA_LOGMF(CPPA_WARNING, self,
"actor with ID " << self->id()
<< " did not reply to a "
"synchronous request message");
auto fhdl = fetch_response_handle(client, hdl);
auto fhdl = fetch_response_handle(self, hdl);
if (fhdl.valid()) fhdl.apply(make_any_tuple(atom("VOID")));
}
} else {
......@@ -321,11 +320,11 @@ class invoke_policy_base {
&& res->template get_as<atom_value>(0) == atom("MESSAGE_ID")) {
auto id = res->template get_as<std::uint64_t>(1);
auto msg_id = message_id::from_integer_value(id);
auto ref_opt = client->sync_handler(msg_id);
// calls client->response_handle() if hdl is a dummy
auto ref_opt = self->sync_handler(msg_id);
// calls self->response_handle() if hdl is a dummy
// argument, forwards hdl otherwise to reply to the
// original request message
auto fhdl = fetch_response_handle(client, hdl);
auto fhdl = fetch_response_handle(self, hdl);
if (ref_opt) {
auto& ref = *ref_opt;
// copy original behavior
......@@ -342,7 +341,7 @@ class invoke_policy_base {
// recursively call invoke_fun on the
// result to correctly handle stuff like
// sync_send(...).then(...).then(...)...
return invoke_fun(client,
return invoke_fun(self,
intermediate,
mutable_mid,
f2,
......@@ -357,7 +356,7 @@ class invoke_policy_base {
res->reset();
} else {
// respond by using the result of 'fun'
auto fhdl = fetch_response_handle(client, hdl);
auto fhdl = fetch_response_handle(self, hdl);
if (fhdl.valid()) fhdl.apply(std::move(*res));
}
}
......@@ -370,47 +369,45 @@ class invoke_policy_base {
// the workflow of handle_message (hm) is as follows:
// - should_skip? if yes: return hm_skip_msg
// - msg is ordinary message? if yes:
// - begin(...) -> prepares a client for message handling
// - client could process message?
// - begin(...) -> prepares a self for message handling
// - self could process message?
// - yes: cleanup()
// - no: revert(...) -> set client back to state it had before begin()
// - no: revert(...) -> set self back to state it had before begin()
template<class Client, class Fun, class Policy>
handle_message_result handle_message(Client* client,
template<class Actor, class Fun>
handle_message_result handle_message(Actor* self,
pointer node,
Fun& fun,
message_id awaited_response,
Policy policy ) {
message_id awaited_response) {
bool handle_sync_failure_on_mismatch = true;
if (Derived::hm_should_skip(node, policy)) {
return hm_skip_msg;
if (dptr()->hm_should_skip(node)) { return hm_skip_msg;
}
switch (this->filter_msg(client, node)) {
switch (this->filter_msg(self, node)) {
default: {
CPPA_CRITICAL("illegal filter result");
}
case normal_exit_signal: {
CPPA_LOGMF(CPPA_DEBUG, client, "dropped normal exit signal");
CPPA_LOGMF(CPPA_DEBUG, self, "dropped normal exit signal");
return hm_drop_msg;
}
case expired_sync_response: {
CPPA_LOGMF(CPPA_DEBUG, client, "dropped expired sync response");
CPPA_LOGMF(CPPA_DEBUG, self, "dropped expired sync response");
return hm_drop_msg;
}
case expired_timeout_message: {
CPPA_LOGMF(CPPA_DEBUG, client, "dropped expired timeout message");
CPPA_LOGMF(CPPA_DEBUG, self, "dropped expired timeout message");
return hm_drop_msg;
}
case non_normal_exit_signal: {
// this message was handled
// by calling client->quit(...)
// by calling self->quit(...)
return hm_msg_handled;
}
case timeout_message: {
handle_timeout(client, fun);
dptr()->handle_timeout(self, fun);
if (awaited_response.valid()) {
client->mark_arrived(awaited_response);
client->remove_handler(awaited_response);
self->mark_arrived(awaited_response);
//FIXME: self->remove_handler(awaited_response);
}
return hm_msg_handled;
}
......@@ -420,46 +417,50 @@ class invoke_policy_base {
}
case sync_response: {
if (awaited_response.valid() && node->mid == awaited_response) {
auto previous_node = Derived::hm_begin(client, node, policy);
auto res = invoke_fun(client,
auto previous_node = dptr()->hm_begin(self, node);
auto res = invoke_fun(self,
node->msg,
node->mid,
fun);
if (!res && handle_sync_failure_on_mismatch) {
CPPA_LOGMF(CPPA_WARNING,
client,
self,
"sync failure occured in actor with ID "
<< client->id());
client->handle_sync_failure();
<< self->id());
self->handle_sync_failure();
}
client->mark_arrived(awaited_response);
client->remove_handler(awaited_response);
Derived::hm_cleanup(client, previous_node, policy);
self->mark_arrived(awaited_response);
//FIXME: self->remove_handler(awaited_response);
dptr()->hm_cleanup(self, previous_node);
return hm_msg_handled;
}
return hm_cache_msg;
}
case ordinary_message: {
if (!awaited_response.valid()) {
auto previous_node = Derived::hm_begin(client, node, policy);
auto res = invoke_fun(client,
auto previous_node = dptr()->hm_begin(self, node);
auto res = invoke_fun(self,
node->msg,
node->mid,
fun);
if (res) {
Derived::hm_cleanup(client, previous_node, policy);
dptr()->hm_cleanup(self, previous_node);
return hm_msg_handled;
}
// no match (restore client members)
Derived::hm_revert(client, previous_node, policy);
// no match (restore self members)
dptr()->hm_revert(self, previous_node);
}
return hm_cache_msg;
}
}
}
Derived* dptr() {
return static_cast<Derived*>(this);
}
};
} } // namespace cppa::policy
#endif // CPPA_NESTABLE_RECEIVE_POLICY_HPP
#endif // CPPA_INVOKE_POLICY_HPP
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_POLICY_MIDDLEMAN_SCHEDULING_HPP
#define CPPA_POLICY_MIDDLEMAN_SCHEDULING_HPP
#include "cppa/singletons.hpp"
#include "cppa/intrusive_ptr.hpp"
#include "cppa/io/middleman.hpp"
#include "cppa/policy/cooperative_scheduling.hpp"
namespace cppa {
namespace policy {
// Actor must implement invoke_message
class middleman_scheduling {
public:
template<class Actor>
class continuation {
public:
typedef intrusive_ptr<Actor> pointer;
continuation(pointer ptr, const message_header& hdr, any_tuple&& msg)
: m_self(std::move(ptr)), m_hdr(hdr), m_data(std::move(msg)) { }
inline void operator()() const {
m_self->invoke_message(m_hdr, move(m_data));
}
private:
pointer m_self;
message_header m_hdr;
any_tuple m_data;
};
using timeout_type = int;
template<class Actor>
timeout_type init_timeout(Actor* self, const util::duration& rel_time) {
// request explicit timeout message
self->request_timeout(rel_time);
return 0; // return some dummy value
}
// this does return nullptr
template<class Actor, typename F>
inline void fetch_messages(Actor*, F) {
// clients cannot fetch messages
}
template<class Actor, typename F>
inline void fetch_messages(Actor* self, F cb, timeout_type) {
// a call to this call is always preceded by init_timeout,
// which will trigger a timeout message
fetch_messages(self, cb);
}
template<class Actor>
inline void launch(Actor*) {
// nothing to do
}
template<class Actor>
void enqueue(Actor* self, const message_header& hdr, any_tuple& msg) {
get_middleman()->run_later(continuation<Actor>{self, hdr, std::move(msg)});
}
};
} // namespace policy
} // namespace cppa
#endif // CPPA_POLICY_MIDDLEMAN_SCHEDULING_HPP
......@@ -44,13 +44,13 @@
#include "cppa/intrusive/single_reader_queue.hpp"
#include "cppa/policy/invoke_policy_base.hpp"
#include "cppa/policy/invoke_policy.hpp"
namespace cppa { namespace detail { class receive_policy; } }
namespace cppa { namespace policy {
class nestable_invoke : public invoke_policy_base<nestable_invoke> {
class nestable_invoke : public invoke_policy<nestable_invoke> {
typedef std::unique_lock<std::mutex> lock_type;
......@@ -62,34 +62,32 @@ class nestable_invoke : public invoke_policy_base<nestable_invoke> {
template<class Client>
static inline pointer hm_begin(Client* client, pointer node) {
auto previous = client->m_current_node;
client->m_current_node = node;
auto previous = client->current_node();
client->current_node(node);
return previous;
}
template<class Client>
static inline void hm_cleanup(Client* client, pointer /*previous*/) {
client->m_current_node = &(client->m_dummy_node);
if (client->has_behavior()) {
client->request_timeout(client->get_behavior().timeout());
}
else client->reset_timeout();
client->current_node(&(client->m_dummy_node));
}
template<class Client>
static inline void hm_revert(Client* client, pointer previous) {
client->m_current_node = previous;
client->current_node(previous);
}
typedef std::chrono::high_resolution_clock::time_point timeout_type;
nestable_invoke() : m_initialized(false) { }
inline void reset_timeout() { }
inline void request_timeout(const util::duration&) { }
template<class Actor>
inline void request_timeout(Actor*, const util::duration&) { }
inline void handle_timeout(behavior& bhvr) { bhvr.handle_timeout(); }
template<class Actor>
inline void handle_timeout(Actor*, behavior& bhvr) {
bhvr.handle_timeout();
}
inline void pop_timeout() { }
......@@ -97,19 +95,7 @@ class nestable_invoke : public invoke_policy_base<nestable_invoke> {
inline bool waits_for_timeout(std::uint32_t) { return false; }
virtual mailbox_element* try_pop() {
return this->m_mailbox.try_pop();
}
mailbox_element* pop() {
wait_for_data();
return try_pop();
}
inline mailbox_element* try_pop(const timeout_type& abs_time) {
return (timed_wait_for_data(abs_time)) ? try_pop() : nullptr;
}
/*
void run_detached() {
auto dthis = util::dptr<Subtype>(this);
dthis->init();
......@@ -120,87 +106,7 @@ class nestable_invoke : public invoke_policy_base<nestable_invoke> {
auto rsn = dthis->planned_exit_reason();
dthis->cleanup(rsn == exit_reason::not_exited ? exit_reason::normal : rsn);
}
inline void initialized(bool value) {
m_initialized = value;
}
bool initialized() const override {
return m_initialized;
}
protected:
typedef threaded combined_type;
void enqueue_impl(typename Base::mailbox_type& mbox,
const message_header& hdr,
any_tuple&& msg) {
auto ptr = this->new_mailbox_element(hdr, std::move(msg));
switch (mbox.enqueue(ptr)) {
case intrusive::first_enqueued: {
lock_type guard(m_mtx);
m_cv.notify_one();
break;
}
default: break;
case intrusive::queue_closed:
if (hdr.id.valid()) {
detail::sync_request_bouncer f{this->exit_reason()};
f(hdr.sender, hdr.id);
}
break;
}
}
void enqueue(const message_header& hdr, any_tuple msg) override {
enqueue_impl(this->m_mailbox, hdr, std::move(msg));
}
timeout_type init_timeout(const util::duration& rel_time) {
auto result = std::chrono::high_resolution_clock::now();
result += rel_time;
return result;
}
mailbox_element* await_message() {
return pop();
}
mailbox_element* await_message(const timeout_type& abs_time) {
return try_pop(abs_time);
}
virtual bool mailbox_empty() {
return this->m_mailbox.empty();
}
bool timed_wait_for_data(const timeout_type& abs_time) {
CPPA_REQUIRE(not this->m_mailbox.closed());
if (mailbox_empty()) {
lock_type guard(m_mtx);
while (mailbox_empty()) {
if (m_cv.wait_until(guard, abs_time) == std::cv_status::timeout) {
return false;
}
}
}
return true;
}
void wait_for_data() {
if (mailbox_empty()) {
lock_type guard(m_mtx);
while (mailbox_empty()) m_cv.wait(guard);
}
}
std::mutex m_mtx;
std::condition_variable m_cv;
private:
bool m_initialized;
*/
};
......
......@@ -5,144 +5,41 @@
#include "cppa/policy/resume_policy.hpp"
namespace cppa {
namespace util {
struct fiber;
} // namespace util
} // namespace cppa
namespace cppa { namespace policy {
// this policy simply forwards calls to @p await_data to the scheduling
// policy and throws an exception whenever @p resume is called;
// it intentionally works only with the no_scheduling policy
class no_resume {
public:
using timeout_type = std::chrono::high_resolution_clock::time_point;
template<class Base>
struct mixin : Base {
template<class Actor>
timeout_type init_timeout(Actor* self, const util::duration& rel_time) {
// request explicit timeout message
self->request_timeout(rel_time);
return 0; // return some dummy value
}
template<typename... Ts>
mixin(Ts&&... args) : Base(std::forward<Ts>(args)...) { }
template<class Actor, typename F>
void fetch_messages(Actor* self, F cb) {
auto e = self->m_mailbox.try_pop();
while (e) {
cb(e);
e = self->m_mailbox.try_pop();
inline resumable::resume_result resume(util::fiber*) {
this->act();
return resumable::done;
}
}
template<class Actor, typename F>
inline void fetch_messages(Actor* self, F cb, timeout_type) {
// a call to this call is always preceded by init_timeout,
// which will trigger a timeout message
fetch_messages(self, cb);
}
};
template<class Actor, typename F>
void try_fetch_messages(Actor* self, F cb) {
// try_fetch and fetch have the same semantics for event-based actors
fetch_messages(self, cb);
template<class Actor>
void await_data(Actor* self) {
self->scheduling_policy().await_data(self);
}
template<class Actor>
resume_result resume(Actor* self, util::fiber*) {
CPPA_LOG_TRACE("id = " << self->id()
<< ", state = " << static_cast<int>(self->state()));
CPPA_REQUIRE( self->state() == actor_state::ready
|| self->state() == actor_state::pending);
auto done_cb = [&]() -> bool {
CPPA_LOG_TRACE("");
if (self->exit_reason() == self->exit_reason::not_exited) {
if (self->planned_exit_reason() == self->exit_reason::not_exited) {
self->planned_exit_reason(exit_reason::normal);
}
self->on_exit();
if (!m_bhvr_stack.empty()) {
planned_exit_reason(exit_reason::not_exited);
return false; // on_exit did set a new behavior
}
cleanup(planned_exit_reason());
}
set_state(actor_state::done);
m_bhvr_stack.clear();
m_bhvr_stack.cleanup();
on_exit();
CPPA_REQUIRE(next_job == nullptr);
return true;
};
CPPA_REQUIRE(next_job == nullptr);
try {
//auto e = m_mailbox.try_pop();
for (auto e = m_mailbox.try_pop(); ; e = m_mailbox.try_pop()) {
//e = m_mailbox.try_pop();
if (e == nullptr) {
CPPA_REQUIRE(next_job == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "no more element in mailbox; going to block");
set_state(actor_state::about_to_block);
std::atomic_thread_fence(std::memory_order_seq_cst);
if (this->m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready:
// interrupted by arriving message
// restore members
CPPA_REQUIRE(m_chained_actor == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"interrupted by arriving message");
break;
case actor_state::blocked:
CPPA_LOGMF(CPPA_DEBUG, self, "set state successfully to blocked");
// done setting actor to blocked
return resume_result::actor_blocked;
default:
CPPA_LOGMF(CPPA_ERROR, self, "invalid state");
CPPA_CRITICAL("invalid state");
};
}
else {
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"mailbox can fetch more");
set_state(actor_state::ready);
CPPA_REQUIRE(m_chained_actor == nullptr);
}
}
else {
CPPA_LOGMF(CPPA_DEBUG, self, "try to invoke message: " << to_string(e->msg));
if (m_bhvr_stack.invoke(m_recv_policy, this, e)) {
CPPA_LOG_DEBUG_IF(m_chained_actor,
"set actor with ID "
<< m_chained_actor->id()
<< " as successor");
if (m_bhvr_stack.empty() && done_cb()) {
CPPA_LOGMF(CPPA_DEBUG, self, "behavior stack empty");
return resume_result::actor_done;
}
m_bhvr_stack.cleanup();
}
}
}
}
catch (actor_exited& what) {
CPPA_LOG_INFO("actor died because of exception: actor_exited, "
"reason = " << what.reason());
if (exit_reason() == exit_reason::not_exited) {
quit(what.reason());
}
}
catch (std::exception& e) {
CPPA_LOG_WARNING("actor died because of exception: "
<< detail::demangle(typeid(e))
<< ", what() = " << e.what());
if (exit_reason() == exit_reason::not_exited) {
quit(exit_reason::unhandled_exception);
}
}
catch (...) {
CPPA_LOG_WARNING("actor died because of an unknown exception");
if (exit_reason() == exit_reason::not_exited) {
quit(exit_reason::unhandled_exception);
}
}
done_cb();
return resume_result::actor_done;
template<class Actor, typename AbsTimeout>
bool await_data(Actor* self, const AbsTimeout& abs_time) {
return self->scheduling_policy().await_data(self, abs_time);
}
};
......
......@@ -33,8 +33,14 @@
#include <mutex>
#include <thread>
#include <chrono>
#include <condition_variable>
#include "cppa/util/fiber.hpp"
#include "cppa/util/duration.hpp"
#include "cppa/policy/scheduling_policy.hpp"
#include "cppa/detail/sync_request_bouncer.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
......@@ -47,6 +53,43 @@ class no_scheduling {
public:
typedef std::chrono::high_resolution_clock::time_point timeout_type;
template<class Actor>
inline timeout_type init_timeout(Actor*, const util::duration& rel_time) {
auto result = std::chrono::high_resolution_clock::now();
result += rel_time;
return result;
}
template<class Actor, typename F>
bool fetch_messages(Actor* self, F cb) {
await_data(self);
fetch_messages_impl(self, cb);
}
template<class Actor, typename F>
bool try_fetch_messages(Actor* self, F cb) {
auto next = [&] { return self->mailbox().try_pop(); };
auto e = next();
if (!e) return false;
do {
cb(e);
e = next();
}
while (e);
return true;
}
template<class Actor, typename F>
timed_fetch_result fetch_messages(Actor* self, F cb, timeout_type abs_time) {
if (!await_data(self, abs_time)) {
return timed_fetch_result::no_message;
}
fetch_messages_impl(self, cb);
return timed_fetch_result::success;
}
template<class Actor>
void enqueue(Actor* self, const message_header& hdr, any_tuple& msg) {
auto ptr = self->new_mailbox_element(hdr, std::move(msg));
......@@ -69,22 +112,29 @@ class no_scheduling {
template<class Actor>
void launch(Actor* self) {
std::thread([=] {
auto rr = resume_result::actor_blocked;
while (rr != resume_result::actor_done) {
wait_for_data();
self->resume();
util::fiber fself;
auto rr = resumable::resume_later;
while (rr != resumable::done) {
await_data(self);
self->resume(&fself);
}
}).detach();
}
private:
template<class Actor>
void await_data(Actor* self) {
if (self->mailbox().empty()) {
lock_type guard(m_mtx);
while (self->mailbox().empty()) m_cv.wait(guard);
}
}
bool timed_wait_for_data(const timeout_type& abs_time) {
CPPA_REQUIRE(not this->m_mailbox.closed());
if (mailbox_empty()) {
template<class Actor>
bool await_data(Actor* self, const timeout_type& abs_time) {
CPPA_REQUIRE(!self->mailbox().closed());
if (self->mailbox().empty()) {
lock_type guard(m_mtx);
while (mailbox_empty()) {
while (self->mailbox().empty()) {
if (m_cv.wait_until(guard, abs_time) == std::cv_status::timeout) {
return false;
}
......@@ -93,11 +143,15 @@ class no_scheduling {
return true;
}
void wait_for_data() {
if (mailbox_empty()) {
lock_type guard(m_mtx);
while (mailbox_empty()) m_cv.wait(guard);
private:
template<class Actor, typename F>
void fetch_messages_impl(Actor* self, F cb) {
auto next = [&] { return self->mailbox().try_pop(); };
for (auto e = next(); e != nullptr; e = next()) {
cb(e);
}
}
std::mutex m_mtx;
......
......@@ -28,24 +28,31 @@
\******************************************************************************/
#ifndef CPPA_SCHEDULED_ACTOR_DUMMY_HPP
#define CPPA_SCHEDULED_ACTOR_DUMMY_HPP
#include "cppa/scheduled_actor.hpp"
namespace cppa { namespace detail {
struct scheduled_actor_dummy : scheduled_actor {
scheduled_actor_dummy();
void enqueue(const message_header&, any_tuple) override;
resume_result resume(util::fiber*) override;
void quit(std::uint32_t) override;
void dequeue(behavior&) override;
void dequeue_response(behavior&, message_id) override;
bool has_behavior() override;
scheduled_actor_type impl_type() override;
#ifndef NOT_PRIORITIZING_HPP
#define NOT_PRIORITIZING_HPP
#include "cppa/mailbox_element.hpp"
#include "cppa/policy/priority_policy.hpp"
namespace cppa {
namespace policy {
struct not_prioritizing {
template<class Actor>
mailbox_element* next_message(Actor* self) {
return self->mailbox().try_pop();
}
template<class Actor, typename F>
bool fetch_messages(Actor*, F) {
//FIXME
}
};
} } // namespace cppa::detail
} // namespace policy
} // namespace cppa
#endif // CPPA_SCHEDULED_ACTOR_DUMMY_HPP
#endif // NOT_PRIORITIZING_HPP
......@@ -62,7 +62,7 @@ class priority_policy {
* for re-scheduling.
*/
template<class Actor, typename F>
bool fetch_messages(Actor* self);
bool fetch_messages(Actor* self, F cb);
};
......
......@@ -31,18 +31,15 @@
#ifndef CPPA_RESUME_POLICY_HPP
#define CPPA_RESUME_POLICY_HPP
#include "cppa/resumable.hpp"
// this header consists all type definitions needed to
// implement the resume_policy trait
namespace cppa { namespace util { class duration; } }
namespace cppa { namespace util { class duration; struct fiber; } }
namespace cppa { namespace policy {
enum class resume_result {
actor_blocked,
actor_done
};
/**
* @brief The resume_policy <b>concept</b> class. Please note that this
* class is <b>not</b> implemented. It only explains the all
......@@ -59,7 +56,7 @@ class resume_policy {
* actor finishes execution.
*/
template<class Actor>
resume_result resume(Actor* self, util::fiber* from);
resumable::resume_result resume(Actor* self, util::fiber* from);
/**
* @brief Waits unconditionally until a new message arrives.
......
......@@ -36,14 +36,14 @@
#include "cppa/util/duration.hpp"
#include "cppa/policy/invoke_policy_base.hpp"
#include "cppa/policy/invoke_policy.hpp"
namespace cppa { namespace policy {
/**
* @brief An actor that is scheduled or otherwise managed.
*/
class sequential_invoke : public invoke_policy_base<sequential_invoke> {
class sequential_invoke : public invoke_policy<sequential_invoke> {
public:
......@@ -53,26 +53,26 @@ class sequential_invoke : public invoke_policy_base<sequential_invoke> {
return node->marked;
}
template<class Client>
static inline pointer hm_begin(Client* client, pointer node) {
auto previous = client->m_current_node;
client->m_current_node = node;
client->push_timeout();
template<class Actor>
inline pointer hm_begin(Actor* self, pointer node) {
auto previous = self->m_current_node;
self->m_current_node = node;
push_timeout();
node->marked = true;
return previous;
}
template<class Client>
static inline void hm_cleanup(Client* client, pointer previous) {
client->m_current_node->marked = false;
client->m_current_node = previous;
template<class Actor>
inline void hm_cleanup(Actor* self, pointer previous) {
self->m_current_node->marked = false;
self->m_current_node = previous;
}
template<class Client>
static inline void hm_revert(Client* client, pointer previous) {
client->m_current_node->marked = false;
client->m_current_node = previous;
client->pop_timeout();
template<class Actor>
inline void hm_revert(Actor* self, pointer previous) {
self->m_current_node->marked = false;
self->m_current_node = previous;
pop_timeout();
}
inline void reset_timeout() {
......@@ -82,22 +82,24 @@ class sequential_invoke : public invoke_policy_base<sequential_invoke> {
}
}
void request_timeout(const util::duration& d) {
template<class Actor>
void request_timeout(Actor* self, const util::duration& d) {
if (!d.valid()) m_has_pending_tout = false;
else {
auto msg = make_any_tuple(atom("SYNC_TOUT"), ++m_pending_tout);
if (d.is_zero()) {
// immediately enqueue timeout message if duration == 0s
this->enqueue({this->address(), this}, std::move(msg));
self->enqueue({self->address(), self}, std::move(msg));
//auto e = this->new_mailbox_element(this, std::move(msg));
//this->m_mailbox.enqueue(e);
}
else this->delayed_send_tuple(this, d, std::move(msg));
else self->delayed_send_tuple(self, d, std::move(msg));
m_has_pending_tout = true;
}
}
inline void handle_timeout(behavior& bhvr) {
template<class Actor>
inline void handle_timeout(Actor*, behavior& bhvr) {
bhvr.handle_timeout();
reset_timeout();
}
......
......@@ -28,64 +28,32 @@
\******************************************************************************/
#ifndef CPPA_THREAD_BASED_ACTOR_HPP
#define CPPA_THREAD_BASED_ACTOR_HPP
#include "cppa/config.hpp"
#include <map>
#include <list>
#include <mutex>
#include <stack>
#include <atomic>
#include <vector>
#include <memory>
#include <cstdint>
#include "cppa/atom.hpp"
#include "cppa/extend.hpp"
#include "cppa/stacked.hpp"
#include "cppa/threaded.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/intrusive_ptr.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/mailbox_element.hpp"
#include "cppa/detail/receive_policy.hpp"
#ifndef CPPA_RESUMABLE_HPP
#define CPPA_RESUMABLE_HPP
namespace cppa {
class self_type;
class scheduler_helper;
/**
* @brief An actor using the blocking API running in its own thread.
* @extends local_actor
*/
class thread_mapped_actor : public extend<local_actor, thread_mapped_actor>::
with<stacked, threaded> {
namespace util {
struct fiber;
} // namespace util
typedef combined_type super;
struct resumable {
public:
enum resume_result {
resume_later,
done
};
thread_mapped_actor();
// intrusive next pointer needed to use
// 'resumable' with 'single_reader_queue'
resumable* next;
thread_mapped_actor(std::function<void()> fun);
virtual ~resumable();
inline void initialized(bool value) { m_initialized = value; }
bool initialized() const override;
private:
bool m_initialized;
virtual resume_result resume(util::fiber*) = 0;
};
typedef intrusive_ptr<thread_mapped_actor> thread_mapped_actor_ptr;
} // namespace cppa
#endif // CPPA_THREAD_BASED_ACTOR_HPP
#endif // CPPA_RESUMABLE_HPP
......@@ -35,7 +35,7 @@
#include <type_traits>
#include "cppa/util/dptr.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/untyped_actor.hpp"
namespace cppa {
......@@ -45,11 +45,11 @@ namespace cppa {
* to initialize the derived actor with its @p init_state member.
* @tparam Derived Direct subclass of @p sb_actor.
*/
template<class Derived, class Base = event_based_actor>
template<class Derived, class Base = untyped_actor>
class sb_actor : public Base {
static_assert(std::is_base_of<event_based_actor, Base>::value,
"Base must be either event_based_actor or a derived type");
static_assert(std::is_base_of<untyped_actor, Base>::value,
"Base must be either untyped_actor or a derived type");
protected:
......@@ -58,11 +58,11 @@ class sb_actor : public Base {
public:
/**
* @brief Overrides {@link event_based_actor::init()} and sets
* @brief Overrides {@link untyped_actor::init()} and sets
* the initial actor behavior to <tt>Derived::init_state</tt>.
*/
void init() override {
become(util::dptr<Derived>(this)->init_state);
behavior make_behavior() override {
return util::dptr<Derived>(this)->init_state;
}
protected:
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_ACTOR_BEHAVIOR_HPP
#define CPPA_ACTOR_BEHAVIOR_HPP
#include <functional>
#include "cppa/config.hpp"
#include "cppa/extend.hpp"
#include "cppa/threadless.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/mailbox_element.hpp"
#include "cppa/message_priority.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa {
class scheduler;
namespace util { struct fiber; }
enum scheduled_actor_type {
context_switching_impl, // enqueued to the job queue on startup
event_based_impl, // not enqueued to the job queue on startup
default_event_based_impl // scheduler enqueues a 'RUN' message on startup
};
class scheduled_actor;
/**
* @brief A base class for cooperatively scheduled actors.
* @extends local_actor
*/
class scheduled_actor : public extend<local_actor>::with<threadless> {
typedef combined_type super;
public:
~scheduled_actor();
/**
* @brief Intrusive next pointer needed by the scheduler's job queue.
*/
scheduled_actor* next;
/**
* @brief Continues execution of this actor.
* @param next_job Denotes the next actor to be resumed, should be
* set in case of chaining.
* @note This member function is called from the scheduler's worker threads.
*/
virtual resume_result resume(util::fiber* from) = 0;
/**
* @brief Called once by the scheduler after actor is initialized,
* but before @p resume is called.
*/
void attach_to_scheduler(scheduler* sched, bool hidden);
/**
* @brief Returns @p false if this actor can be disposed immediately
* after construction, @p true otherwise.
*/
virtual bool has_behavior() = 0;
/**
* @brief Denotes wheter this actor is enqueued to the scheduler's
* job queue on startup.
*/
virtual scheduled_actor_type impl_type() = 0;
/**
* @brief Returns @p true if this actor is ignored by
* {@link await_all_actors_done()}, false otherwise.
*/
inline bool is_hidden() const;
virtual void run_detached();
void enqueue(const message_header&, any_tuple) override;
protected:
scheduled_actor(actor_state init_state, bool enable_chained_send);
void cleanup(std::uint32_t reason) override;
typedef intrusive::single_reader_queue<mailbox_element, detail::disposer>
mailbox_type;
actor_state compare_exchange_state(actor_state expected, actor_state desired);
inline void set_state(actor_state new_value) {
m_state.store(new_value);
}
inline actor_state state() const {
return m_state;
}
bool initialized() const;
private:
bool enqueue_impl(actor_state, const message_header&, any_tuple&&);
std::atomic<actor_state> m_state;
protected:
scheduler* m_scheduler;
bool m_hidden;
};
typedef intrusive_ptr<scheduled_actor> scheduled_actor_ptr;
inline bool scheduled_actor::is_hidden() const {
return m_hidden;
}
} // namespace cppa
#endif // CPPA_ACTOR_BEHAVIOR_HPP
......@@ -41,8 +41,8 @@
#include "cppa/actor.hpp"
#include "cppa/channel.hpp"
#include "cppa/cow_tuple.hpp"
#include "cppa/resumable.hpp"
#include "cppa/attachable.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/spawn_options.hpp"
#include "cppa/util/duration.hpp"
......@@ -52,7 +52,7 @@ namespace cppa {
class untyped_actor;
class scheduled_actor;
class scheduler_helper;
class event_based_actor;
class untyped_actor;
typedef intrusive_ptr<scheduled_actor> scheduled_actor_ptr;
namespace detail { class singleton_manager; } // namespace detail
......@@ -83,12 +83,9 @@ class scheduler {
public:
typedef std::function<void(local_actor*)> init_callback;
typedef std::function<behavior(untyped_actor*)> actor_fun;
const actor& printer() const;
actor printer() const;
virtual void enqueue(scheduled_actor*) = 0;
virtual void enqueue(resumable*) = 0;
/**
* @brief Informs the scheduler about a converted context
......@@ -128,33 +125,13 @@ class scheduler {
//TODO: delayed_send_helper()->enqueue(nullptr, std::move(tup));
}
/**
* @brief Executes @p ptr in this scheduler.
*/
virtual local_actor_ptr exec(spawn_options opts,
scheduled_actor_ptr ptr) = 0;
/**
* @brief Creates a new actor from @p actor_behavior and executes it
* in this scheduler.
*/
virtual local_actor_ptr exec(spawn_options opts,
init_callback init_cb,
actor_fun actor_behavior) = 0;
template<typename F, typename T, typename... Ts>
local_actor_ptr exec(spawn_options opts, init_callback cb,
F f, T&& a0, Ts&&... as) {
return this->exec(opts, cb, std::bind(f, std::forward<T>(a0), std::forward<Ts>(as)...));
}
private:
static scheduler* create_singleton();
inline void dispose() { delete this; }
actor& delayed_send_helper();
actor delayed_send_helper();
scheduler_helper* m_helper;
......
......@@ -25,131 +25,55 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_SCOPED_ACTOR_HPP
#define CPPA_SCOPED_ACTOR_HPP
#include "cppa/behavior.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/blocking_untyped_actor.hpp"
namespace cppa {
class scoped_actor {
public:
/**
* @brief Dequeues the next message from the mailbox that
* is matched by given behavior.
*/
template<typename... Ts>
void receive(Ts&&... args);
/**
* @brief Receives messages in an endless loop.
* Semantically equal to: <tt>for (;;) { receive(...); }</tt>
*/
template<typename... Ts>
void receive_loop(Ts&&... args);
/**
* @brief Receives messages as in a range-based loop.
*
* Semantically equal to:
* <tt>for ( ; begin != end; ++begin) { receive(...); }</tt>.
*
* <b>Usage example:</b>
* @code
* int i = 0;
* receive_for(i, 10) (
* on(atom("get")) >> [&]() -> any_tuple { return {"result", i}; }
* );
* @endcode
* @param begin First value in range.
* @param end Last value in range (excluded).
* @returns A functor implementing the loop.
*/
template<typename T>
detail::receive_for_helper<T> receive_for(T& begin, const T& end);
/**
* @brief Receives messages as long as @p stmt returns true.
*
* Semantically equal to: <tt>while (stmt()) { receive(...); }</tt>.
*
* <b>Usage example:</b>
* @code
* int i = 0;
* receive_while([&]() { return (++i <= 10); })
* (
* on<int>() >> int_fun,
* on<float>() >> float_fun
* );
* @endcode
* @param stmt Lambda expression, functor or function returning a @c bool.
* @returns A functor implementing the loop.
*/
template<typename Statement>
detail::receive_while_helper<Statement> receive_while(Statement&& stmt);
/**
* @brief Receives messages until @p stmt returns true.
*
* Semantically equal to: <tt>do { receive(...); } while (stmt() == false);</tt>
*
* <b>Usage example:</b>
* @code
* int i = 0;
* do_receive
* (
* on<int>() >> int_fun,
* on<float>() >> float_fun
* )
* .until([&]() { return (++i >= 10); };
* @endcode
* @param bhvr Denotes the actor's response the next incoming message.
* @returns A functor providing the @c until member function.
*/
template<typename... Ts>
detail::do_receive_helper do_receive(Ts&&... args);
private:
intrusive_ptr<thread_mapped_actor> m_self;
public:
scoped_actor();
~scoped_actor();
inline blocking_untyped_actor* operator->() const {
return m_self.get();
}
inline blocking_untyped_actor& operator*() const {
return *m_self;
}
inline blocking_untyped_actor* get() const {
return m_self.get();
}
operator channel() const {
return get();
}
operator actor() const {
return get();
}
operator actor_addr() const {
return get()->address();
}
private:
intrusive_ptr<blocking_untyped_actor> m_self;
};
/******************************************************************************
* inline and template function implementations *
******************************************************************************/
template<typename... Ts>
void scoped_actor::receive(Ts&&... args) {
m_self->receive(std::forward<Ts(args)...);
}
template<typename... Ts>
void scoped_actor::receive_loop(Ts&&... args) {
m_self->receive_loop(std::forward<Ts(args)...);
}
template<typename T>
detail::receive_for_helper<T> scoped_actor::receive_for(T& begin, const T& end) {
m_self->receive_for(begin, end);
}
template<typename Statement>
detail::receive_while_helper<Statement> scoped_actor::receive_while(Statement&& stmt) {
m_self->receive_while(std::forward<Statement(stmt));
}
template<typename... Ts>
detail::do_receive_helper scoped_actor::do_receive(Ts&&... args) {
m_self->do_receive(std::forward<Ts(args)...);
}
} // namespace cppa
#endif // CPPA_SCOPED_ACTOR_HPP
......@@ -39,17 +39,11 @@
#include "cppa/spawn_options.hpp"
#include "cppa/detail/proper_actor.hpp"
#include "cppa/detail/functor_based_actor.hpp"
#include "cppa/detail/functor_based_blocking_actor.hpp"
namespace cppa {
/** @cond PRIVATE */
constexpr bool unbound_spawn_options(spawn_options opts) {
return !has_monitor_flag(opts) && !has_link_flag(opts);
}
/** @endcond */
/**
* @ingroup ActorCreation
* @{
......@@ -58,17 +52,21 @@ constexpr bool unbound_spawn_options(spawn_options opts) {
/**
* @brief Spawns an actor of type @p Impl.
* @param args Constructor arguments.
* @tparam Impl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @tparam Impl Subtype of {@link untyped_actor} or {@link sb_actor}.
* @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @returns An {@link actor} to the spawned {@link actor}.
*/
template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
template<class Impl, spawn_options Options, typename... Ts>
actor spawn(Ts&&... args) {
static_assert(std::is_base_of<event_based_actor, Impl>::value,
"Impl is not a derived type of event_based_actor");
static_assert(unbound_spawn_options(Options),
static_assert(std::is_base_of<untyped_actor, Impl>::value ||
(std::is_base_of<blocking_untyped_actor, Impl>::value &&
has_blocking_api_flag(Options)),
"Impl is not a derived type of untyped_actor or "
"is a derived type of blocking_untyped_actor but "
"blocking_api_flag is missing");
static_assert(is_unbound(Options),
"top-level spawns cannot have monitor or link flag");
static_assert(unbound_spawn_options(Options),
static_assert(is_unbound(Options),
"top-level spawns cannot have monitor or link flag");
using scheduling_policy = typename std::conditional<
has_detach_flag(Options),
......@@ -100,6 +98,8 @@ actor spawn(Ts&&... args) {
resume_policy,
invoke_policy>;
auto ptr = make_counted<proper_impl>(std::forward<Ts>(args)...);
ptr->launch();
return ptr;
/*
scheduled_actor_ptr ptr;
if (has_priority_aware_flag(Options)) {
......@@ -121,7 +121,8 @@ actor spawn(Ts&&... args) {
* @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @returns An {@link actor} to the spawned {@link actor}.
*/
template<spawn_options Options = no_spawn_options, typename... Ts>
//template<spawn_options Options = no_spawn_options, typename... Ts>
template<spawn_options Options, typename... Ts>
actor spawn(Ts&&... args) {
static_assert(sizeof...(Ts) > 0, "too few arguments provided");
using base_class = typename std::conditional<
......@@ -129,13 +130,7 @@ actor spawn(Ts&&... args) {
detail::functor_based_blocking_actor,
detail::functor_based_actor
>::type;
return spawn<base_class>(std::forward<Ts>(args)...);
using impl = detail::proper_actor<untyped_actor,
scheduling_policy,
priority_policy,
resume_policy,
invoke_policy>;
return make_counted<impl>();
return spawn<base_class, Options>(std::forward<Ts>(args)...);
}
/**
......@@ -163,7 +158,7 @@ actor spawn_in_group(const group_ptr& grp, Ts&&... args) {
/**
* @brief Spawns an actor of type @p Impl that immediately joins @p grp.
* @param args Constructor arguments.
* @tparam Impl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @tparam Impl Subtype of {@link untyped_actor} or {@link sb_actor}.
* @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @returns An {@link actor} to the spawned {@link actor}.
* @note The spawned has joined the group before this function returns.
......
......@@ -173,6 +173,21 @@ constexpr bool has_blocking_api_flag(spawn_options opts) {
/** @} */
/** @cond PRIVATE */
constexpr bool is_unbound(spawn_options opts) {
return !has_monitor_flag(opts) && !has_link_flag(opts);
}
constexpr spawn_options make_unbound(spawn_options opts) {
return static_cast<spawn_options>(
(static_cast<int>(opts)
& ~static_cast<int>(spawn_options::link_flag))
& ~static_cast<int>(spawn_options::monitor_flag));
}
/** @endcond */
} // namespace cppa
#endif // CPPA_SPAWN_OPTIONS_HPP
......@@ -72,6 +72,10 @@ inline std::string to_string(const actor& what) {
return detail::to_string_impl(what);
}
inline std::string to_string(const actor_addr& what) {
return detail::to_string_impl(what);
}
inline std::string to_string(const group_ptr& what) {
return detail::to_string_impl(what);
}
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_TYPED_ACTOR_HPP
......
......@@ -34,7 +34,7 @@
#include "cppa/replies_to.hpp"
#include "cppa/typed_behavior.hpp"
#include "cppa/message_future.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/untyped_actor.hpp"
#include "cppa/detail/typed_actor_util.hpp"
......@@ -44,7 +44,7 @@ template<typename... Signatures>
class typed_actor_ptr;
template<typename... Signatures>
class typed_actor : public event_based_actor {
class typed_actor : public untyped_actor {
public:
......
......@@ -25,103 +25,30 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#ifndef CPPA_UNTYPED_ACTOR_HPP
#define CPPA_UNTYPED_ACTOR_HPP
#include "cppa/extend.hpp"
#include "cppa/stackless.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/behavior_stack_based.hpp"
namespace cppa {
/**
* @extends local_actor
*/
class untyped_actor : public local_actor {
bool has_behavior() {
return this->m_bhvr_stack.empty() == false;
}
void unbecome() {
this->m_bhvr_stack.pop_async_back();
}
/**
* @brief Sets the actor's behavior and discards the previous behavior
* unless {@link keep_behavior} is given as first argument.
*/
template<typename T, typename... Ts>
inline typename std::enable_if<
!is_behavior_policy<typename util::rm_const_and_ref<T>::type>::value,
void
>::type
become(T&& arg, Ts&&... args) {
do_become(match_expr_convert(std::forward<T>(arg),
std::forward<Ts>(args)...),
true);
}
template<bool Discard, typename... Ts>
inline void become(behavior_policy<Discard>, Ts&&... args) {
do_become(match_expr_convert(std::forward<Ts>(args)...), Discard);
}
void become_waiting_for(behavior bhvr, message_id mf) {
if (bhvr.timeout().valid()) {
this->reset_timeout();
this->request_timeout(bhvr.timeout());
}
this->m_bhvr_stack.push_back(std::move(bhvr), mf);
}
void do_become(behavior&& bhvr, bool discard_old) {
this->reset_timeout();
this->request_timeout(bhvr.timeout());
if (discard_old) this->m_bhvr_stack.pop_async_back();
this->m_bhvr_stack.push_back(std::move(bhvr));
}
inline bool has_behavior() const {
return this->m_bhvr_stack.empty() == false;
}
inline behavior& get_behavior() {
CPPA_REQUIRE(this->m_bhvr_stack.empty() == false);
return this->m_bhvr_stack.back();
}
inline void handle_timeout(behavior& bhvr) {
CPPA_REQUIRE(bhvr.timeout().valid());
this->reset_timeout();
bhvr.handle_timeout();
if (this->m_bhvr_stack.empty() == false) {
this->request_timeout(get_behavior().timeout());
}
}
void exec_bhvr_stack() {
while (!m_bhvr_stack.empty()) {
m_bhvr_stack.exec(m_recv_policy, util::dptr<Subtype>(this));
}
}
inline detail::behavior_stack& bhvr_stack() {
return m_bhvr_stack;
}
inline optional<behavior&> sync_handler(message_id msg_id) {
return m_bhvr_stack.sync_handler(msg_id);
}
class untyped_actor : public extend<local_actor>::with<mailbox_based,
behavior_stack_based> {
protected:
// allows actors to keep previous behaviors and enables unbecome()
detail::behavior_stack m_bhvr_stack;
virtual behavior make_behavior() = 0;
void forward_to(const actor& other);
};
......
......@@ -110,17 +110,17 @@ constexpr int min_req_interval = 10;
constexpr int max_req_interval = 300;
const actor_ostream& print(const char* color_code, const char* actor_name) {
return aout << color_code << actor_name << " (id = " << self->id() << "): ";
return aout << color_code << actor_name;// << " (id = " << self->id() << "): ";
}
} // namespace <anonymous>
// provides print utility and each base_actor has a parent
class base_actor : public event_based_actor {
class base_actor : public untyped_actor {
protected:
base_actor(actor_ptr parent, std::string name, std::string color_code)
base_actor(actor parent, std::string name, std::string color_code)
: m_parent(std::move(parent))
, m_name(std::move(name))
, m_color(std::move(color_code)) { }
......@@ -133,7 +133,7 @@ class base_actor : public event_based_actor {
print() << "on_exit" << color::reset_endl;
}
actor_ptr m_parent;
actor m_parent;
private:
......@@ -147,19 +147,19 @@ class client_job : public base_actor {
public:
client_job(actor_ptr parent)
client_job(actor parent)
: base_actor(std::move(parent), "client_job", color::blue) { }
protected:
void init() override {
behavior make_behavior() override {
print() << "init" << color::reset_endl;
send(m_parent,
atom("read"),
"http://www.example.com/index.html",
static_cast<uint64_t>(0),
static_cast<uint64_t>(4095));
become (
return (
on(atom("reply"), arg_match) >> [=](const util::buffer& buf) {
print() << "successfully received "
<< buf.size()
......@@ -181,18 +181,18 @@ class client : public base_actor {
public:
client(const actor_ptr& parent)
client(const actor& parent)
: base_actor(parent, "client", color::green), m_count(0) { }
protected:
void init() override {
behavior make_behavior() override {
using std::chrono::milliseconds;
link_to(m_parent);
print() << "init" << color::reset_endl;
// start 'loop'
send(this, atom("next"));
become (
return (
on(atom("next")) >> [=] {
print() << "spawn new client_job (nr. "
<< ++m_count
......@@ -203,7 +203,7 @@ class client : public base_actor {
spawn<client_job, detached+linked>(m_parent);
// compute random delay until next job is launched
auto delay = (rand() + min_req_interval) % max_req_interval;
delayed_send(self, milliseconds(delay), atom("next"));
delayed_send(this, milliseconds(delay), atom("next"));
}
);
}
......@@ -219,17 +219,17 @@ class curl_worker : public base_actor {
public:
curl_worker(const actor_ptr& parent)
curl_worker(const actor& parent)
: base_actor(parent, "curl_worker", color::yellow) { }
protected:
void init() override {
behavior make_behavior() override {
print() << "init" << color::reset_endl;
m_curl = curl_easy_init();
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, &curl_worker::cb);
curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1);
become (
return (
on(atom("read"), arg_match)
>> [=](const std::string& fname, uint64_t offset, uint64_t range)
-> cow_tuple<atom_value, util::buffer> {
......@@ -314,31 +314,32 @@ class curl_master : public base_actor {
protected:
void init() override {
behavior make_behavior() override {
print() << "init" << color::reset_endl;
// spawn workers
for(size_t i = 0; i < num_curl_workers; ++i) {
m_idle_worker.push_back(spawn<curl_worker, detached+linked>(this));
}
auto worker_finished = [=] {
actor_ptr sender = self->last_sender();
m_busy_worker.erase(std::find(m_busy_worker.begin(),
m_busy_worker.end(),
sender));
m_idle_worker.push_back(sender);
auto sender = last_sender();
auto i = std::find(m_busy_worker.begin(),
m_busy_worker.end(),
sender);
m_idle_worker.push_back(*i);
m_busy_worker.erase(i);
print() << "worker is done" << color::reset_endl;
};
print() << "spawned "
<< m_idle_worker.size()
<< " worker"
<< color::reset_endl;
become (
return (
on(atom("read"), arg_match) >> [=](const std::string&,
uint64_t,
uint64_t) {
print() << "received {'read'}" << color::reset_endl;
// forward job to first idle worker
actor_ptr worker = m_idle_worker.front();
actor worker = m_idle_worker.front();
m_idle_worker.erase(m_idle_worker.begin());
m_busy_worker.push_back(worker);
forward_to(worker);
......@@ -364,8 +365,8 @@ class curl_master : public base_actor {
private:
std::vector<actor_ptr> m_idle_worker;
std::vector<actor_ptr> m_busy_worker;
std::vector<actor> m_idle_worker;
std::vector<actor> m_busy_worker;
};
......@@ -394,7 +395,7 @@ int main() {
while (!shutdown_flag) { sleep(1); }
aout << color::cyan << "received CTRL+C" << color::reset_endl;
// shutdown actors
send_exit(master, exit_reason::user_shutdown);
anon_send_exit(master, exit_reason::user_shutdown);
// await actors
act.sa_handler = [](int) { abort(); };
set_sighandler();
......
......@@ -5,11 +5,11 @@
using namespace std;
using namespace cppa;
void mirror() {
behavior mirror(untyped_actor* self) {
// wait for messages
become (
return (
// invoke this lambda expression if we receive a string
on_arg_match >> [](const string& what) -> string {
on_arg_match >> [=](const string& what) -> string {
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout << what << endl;
// terminates this actor ('become' otherwise loops forever)
......@@ -20,11 +20,11 @@ void mirror() {
);
}
void hello_world(const actor_ptr& buddy) {
void hello_world(untyped_actor* self, const actor& buddy) {
// send "Hello World!" to our buddy ...
sync_send(buddy, "Hello World!").then(
self->sync_send(buddy, "Hello World!").then(
// ... and wait for a response
on_arg_match >> [](const string& what) {
on_arg_match >> [=](const string& what) {
// prints "!dlroW olleH"
aout << what << endl;
}
......
......@@ -13,9 +13,9 @@ using std::endl;
using namespace cppa;
// implementation using the blocking API
void blocking_math_fun() {
void blocking_math_fun(blocking_untyped_actor* self) {
bool done = false;
do_receive (
self->do_receive (
// "arg_match" matches the parameter types of given lambda expression
// thus, it's equal to
// - on<atom("plus"), int, int>()
......@@ -34,38 +34,38 @@ void blocking_math_fun() {
).until(gref(done));
}
void calculator() {
void calculator(untyped_actor* self) {
// execute this behavior until actor terminates
become (
self->become (
on(atom("plus"), arg_match) >> [](int a, int b) {
return make_cow_tuple(atom("result"), a + b);
},
on(atom("minus"), arg_match) >> [](int a, int b) {
return make_cow_tuple(atom("result"), a - b);
},
on(atom("quit")) >> [] {
on(atom("quit")) >> [=] {
// terminate actor with normal exit reason
self->quit();
}
);
}
void tester(const actor_ptr& testee) {
void tester(untyped_actor* self, const actor& testee) {
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([] {
self->on_sync_failure([=] {
aout << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
sync_send(testee, atom("plus"), 2, 1).then(
self->sync_send(testee, atom("plus"), 2, 1).then(
on(atom("result"), 3) >> [=] {
// second test: 2 - 1 = 1
sync_send(testee, atom("minus"), 2, 1).then(
self->sync_send(testee, atom("minus"), 2, 1).then(
on(atom("result"), 1) >> [=] {
// both tests succeeded
aout << "AUT (actor under test) seems to be ok" << endl;
send(testee, atom("quit"));
self->send(testee, atom("quit"));
}
);
}
......@@ -78,4 +78,3 @@ int main() {
shutdown();
return 0;
}
......@@ -53,20 +53,20 @@ void draw_kirby(const animation_step& animation) {
}
// uses a message-based loop to iterate over all animation steps
void dancing_kirby() {
void dancing_kirby(untyped_actor* self) {
// let's get it started
send(self, atom("Step"), size_t{0});
become (
on(atom("Step"), array_size(animation_steps)) >> [] {
self->send(self, atom("Step"), size_t{0});
self->become (
on(atom("Step"), array_size(animation_steps)) >> [=] {
// we've printed all animation steps (done)
cout << endl;
self->quit();
},
on(atom("Step"), arg_match) >> [](size_t step) {
on(atom("Step"), arg_match) >> [=](size_t step) {
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
delayed_send(self, std::chrono::milliseconds(150), atom("Step"), step + 1);
self->delayed_send(self, std::chrono::milliseconds(150), atom("Step"), step + 1);
}
);
}
......
......@@ -17,21 +17,21 @@ using namespace std;
using namespace cppa;
// either taken by a philosopher or available
void chopstick() {
become(
on(atom("take"), arg_match) >> [=](const actor_ptr& philos) {
void chopstick(untyped_actor* self) {
self->become(
on(atom("take"), arg_match) >> [=](const actor& philos) {
// tell philosopher it took this chopstick
send(philos, atom("taken"), self);
self->send(philos, atom("taken"), self);
// await 'put' message and reject other 'take' messages
become(
self->become(
// allows us to return to the previous behavior
keep_behavior,
on(atom("take"), arg_match) >> [=](const actor_ptr& other) {
send(other, atom("busy"), self);
on(atom("take"), arg_match) >> [=](const actor& other) {
self->send(other, atom("busy"), self);
},
on(atom("put"), philos) >> [=] {
// return to previous behaivor, i.e., await next 'take'
unbecome();
self->unbecome();
}
);
}
......@@ -71,11 +71,11 @@ void chopstick() {
* [ X = right => Y = left ]
*/
struct philosopher : event_based_actor {
struct philosopher : untyped_actor {
std::string name; // the name of this philosopher
actor_ptr left; // left chopstick
actor_ptr right; // right chopstick
actor left; // left chopstick
actor right; // right chopstick
// note: we have to define all behaviors in the constructor because
// non-static member initialization are not (yet) implemented in GCC
......@@ -85,7 +85,7 @@ struct philosopher : event_based_actor {
behavior eating;
// wait for second chopstick
behavior waiting_for(const actor_ptr& what) {
behavior waiting_for(const actor& what) {
return (
on(atom("taken"), what) >> [=] {
aout << name
......@@ -106,7 +106,7 @@ struct philosopher : event_based_actor {
);
}
philosopher(const std::string& n, const actor_ptr& l, const actor_ptr& r)
philosopher(const std::string& n, const actor& l, const actor& r)
: name(n), left(l), right(r) {
// a philosopher that receives {eat} stops thinking and becomes hungry
thinking = (
......@@ -124,18 +124,18 @@ struct philosopher : event_based_actor {
on(atom("taken"), right) >> [=] {
become(waiting_for(left));
},
on<atom("busy"), actor_ptr>() >> [=] {
on<atom("busy"), actor>() >> [=] {
become(denied);
}
);
// philosopher was not able to obtain the first chopstick
denied = (
on(atom("taken"), arg_match) >> [=](const actor_ptr& ptr) {
on(atom("taken"), arg_match) >> [=](const actor& ptr) {
send(ptr, atom("put"), this);
send(this, atom("eat"));
become(thinking);
},
on<atom("busy"), actor_ptr>() >> [=] {
on<atom("busy"), actor>() >> [=] {
send(this, atom("eat"));
become(thinking);
}
......@@ -153,17 +153,17 @@ struct philosopher : event_based_actor {
);
}
void init() {
behavior make_behavior() override {
// start thinking
send(this, atom("think"));
// philosophers start to think after receiving {think}
become (
return (
on(atom("think")) >> [=] {
aout << name << " starts to think\n";
delayed_send(this, seconds(5), atom("eat"));
become(thinking);
}
);
// start thinking
send(this, atom("think"));
}
};
......@@ -171,7 +171,7 @@ struct philosopher : event_based_actor {
int main(int, char**) {
// create five chopsticks
aout << "chopstick ids are:";
std::vector<actor_ptr> chopsticks;
std::vector<actor> chopsticks;
for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(spawn(chopstick));
aout << " " << chopsticks.back()->id();
......
......@@ -26,15 +26,15 @@ using namespace cppa;
using namespace cppa::placeholders;
// our "service"
void calculator() {
become (
void calculator(untyped_actor* self) {
self->become (
on(atom("plus"), arg_match) >> [](int a, int b) -> any_tuple {
return {atom("result"), a + b};
return make_any_tuple(atom("result"), a + b);
},
on(atom("minus"), arg_match) >> [](int a, int b) -> any_tuple {
return {atom("result"), a - b};
return make_any_tuple(atom("result"), a - b);
},
on(atom("quit")) >> [=]() {
on(atom("quit")) >> [=] {
self->quit();
}
);
......@@ -49,12 +49,12 @@ inline string trim(std::string s) {
return s;
}
void client_bhvr(const string& host, uint16_t port, const actor_ptr& server) {
void client_bhvr(untyped_actor* self, const string& host, uint16_t port, const actor& server) {
// recover from sync failures by trying to reconnect to server
if (!self->has_sync_failure_handler()) {
self->on_sync_failure([=] {
aout << "*** lost connection to " << host << ":" << port << endl;
client_bhvr(host, port, nullptr);
client_bhvr(self, host, port, nullptr);
});
}
// connect to server if needed
......@@ -64,17 +64,17 @@ void client_bhvr(const string& host, uint16_t port, const actor_ptr& server) {
auto new_serv = remote_actor(host, port);
self->monitor(new_serv);
aout << "reconnection succeeded" << endl;
client_bhvr(host, port, new_serv);
client_bhvr(self, host, port, new_serv);
return;
}
catch (exception&) {
aout << "connection failed, try again in 3s" << endl;
delayed_send(self, chrono::seconds(3), atom("reconnect"));
self->delayed_send(self, chrono::seconds(3), atom("reconnect"));
}
}
become (
self->become (
on_arg_match.when(_x1.in({atom("plus"), atom("minus")}) && gval(server) != nullptr) >> [=](atom_value op, int lhs, int rhs) {
sync_send_tuple(server, self->last_dequeued()).then(
self->sync_send_tuple(server, self->last_dequeued()).then(
on(atom("result"), arg_match) >> [=](int result) {
aout << lhs << " "
<< to_string(op) << " "
......@@ -85,14 +85,14 @@ void client_bhvr(const string& host, uint16_t port, const actor_ptr& server) {
},
on(atom("DOWN"), arg_match) >> [=](uint32_t) {
aout << "*** server down, try to reconnect ..." << endl;
client_bhvr(host, port, nullptr);
client_bhvr(self, host, port, nullptr);
},
on(atom("rebind"), arg_match) >> [=](const string& host, uint16_t port) {
aout << "*** rebind to new server: " << host << ":" << port << endl;
client_bhvr(host, port, nullptr);
client_bhvr(self, host, port, nullptr);
},
on(atom("reconnect")) >> [=] {
client_bhvr(host, port, nullptr);
client_bhvr(self, host, port, nullptr);
}
);
}
......@@ -112,7 +112,7 @@ void client_repl(const string& host, uint16_t port) {
line = trim(std::move(line)); // ignore leading and trailing whitespaces
if (line == "quit") {
// force client to quit
send_exit(client, exit_reason::user_shutdown);
anon_send_exit(client, exit_reason::user_shutdown);
return;
}
// the STL way of line.starts_with("connect")
......@@ -123,7 +123,7 @@ void client_repl(const string& host, uint16_t port) {
auto lport = std::stoul(sport);
if (lport < std::numeric_limits<uint16_t>::max()) {
auto port = static_cast<uint16_t>(lport);
send(client, atom("rebind"), move(host), port);
anon_send(client, atom("rebind"), move(host), port);
}
else {
aout << lport << " is not a valid port" << endl;
......@@ -159,7 +159,7 @@ void client_repl(const string& host, uint16_t port) {
auto rhs = toint(rsub);
if (lhs && rhs) {
auto op = (*pos == '+') ? atom("plus") : atom("minus");
send(client, op, *lhs, *rhs);
anon_send(client, op, *lhs, *rhs);
}
}
else if (!success) {
......
......@@ -42,22 +42,22 @@ any_tuple split_line(const line& l) {
return any_tuple::view(std::move(result));
}
void client(const string& name) {
become (
void client(untyped_actor* self, const string& name) {
self->become (
on(atom("broadcast"), arg_match) >> [=](const string& message) {
for(auto& dest : self->joined_groups()) {
send(dest, name + ": " + message);
self->send(dest, name + ": " + message);
}
},
on(atom("join"), arg_match) >> [=](const group_ptr& what) {
for (auto g : self->joined_groups()) {
cout << "*** leave " << to_string(g) << endl;
send(g, name + " has left the chatroom");
self->send(self, g, name + " has left the chatroom");
self->leave(g);
}
cout << "*** join " << to_string(what) << endl;
self->join(what);
send(what, name + " has entered the chatroom");
self->send(self, what, name + " has entered the chatroom");
},
on<string>() >> [=](const string& txt) {
// don't print own messages
......@@ -104,7 +104,7 @@ int main(int argc, char** argv) {
try {
auto g = group::get(group_id.substr(0, p),
group_id.substr(p + 1));
send(client_actor, atom("join"), g);
anon_send(client_actor, atom("join"), g);
}
catch (exception& e) {
ostringstream err;
......@@ -120,7 +120,7 @@ int main(int argc, char** argv) {
match_each (lines, eof, split_line) (
on("/join", arg_match) >> [&](const string& mod, const string& id) {
try {
send(client_actor, atom("join"), group::get(mod, id));
anon_send(client_actor, atom("join"), group::get(mod, id));
}
catch (exception& e) {
cerr << "*** exception: " << to_verbose_string(e) << endl;
......@@ -137,12 +137,12 @@ int main(int argc, char** argv) {
},
others() >> [&] {
if (!s_last_line.empty()) {
send(client_actor, atom("broadcast"), s_last_line);
anon_send(client_actor, atom("broadcast"), s_last_line);
}
}
);
// force actor to quit
send_exit(client_actor, exit_reason::user_shutdown);
anon_send_exit(client_actor, exit_reason::user_shutdown);
await_all_actors_done();
shutdown();
return 0;
......
......@@ -42,12 +42,12 @@ bool operator==( const foo2& lhs, const foo2& rhs ) {
}
// receives `remaining` messages
void testee(size_t remaining) {
void testee(untyped_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(remaining - 1);
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
become (
self->become (
// note: we sent a foo_pair2, but match on foo_pair
// that's safe because both are aliases for std::pair<int, int>
on<foo_pair>() >> [=](const foo_pair& val) {
......@@ -113,11 +113,13 @@ int main(int, char**) {
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
// send t a foo
send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
send(t, foo_pair2{3, 4});
{
scoped_actor self;
// send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
self->send(t, foo_pair2{3, 4});
}
await_all_actors_done();
shutdown();
return 0;
......
......@@ -39,9 +39,9 @@ bool operator==(const foo& lhs, const foo& rhs) {
&& lhs.b() == rhs.b();
}
void testee() {
become (
on<foo>() >> [](const foo& val) {
void testee(untyped_actor* self) {
self->become (
on<foo>() >> [=](const foo& val) {
aout << "foo("
<< val.a() << ", "
<< val.b() << ")"
......@@ -56,8 +56,11 @@ int main(int, char**) {
// we pass those to the announce function as { getter, setter } pairs.
announce<foo>(make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
auto t = spawn(testee);
send(t, foo{1, 2});
{
scoped_actor self;
auto t = spawn(testee);
self->send(t, foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
......
......@@ -46,9 +46,9 @@ typedef int (foo::*foo_getter)() const;
// a member function pointer to set an attribute of foo
typedef void (foo::*foo_setter)(int);
void testee() {
become (
on<foo>() >> [](const foo& val) {
void testee(untyped_actor* self) {
self->become (
on<foo>() >> [=](const foo& val) {
aout << "foo("
<< val.a() << ", "
<< val.b() << ")"
......@@ -78,7 +78,10 @@ int main(int, char**) {
static_cast<foo_setter>(&foo::b)));
// spawn a new testee and send it a foo
send(spawn(testee), foo{1, 2});
{
scoped_actor self;
self->send(spawn(testee), foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
......
......@@ -79,12 +79,12 @@ bool operator==(const baz& lhs, const baz& rhs) {
}
// receives `remaining` messages
void testee(size_t remaining) {
void testee(untyped_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(remaining - 1);
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
become (
self->become (
on<bar>() >> [=](const bar& val) {
aout << "bar(foo("
<< val.f.a() << ", "
......@@ -127,8 +127,11 @@ int main(int, char**) {
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
send(t, bar{foo{1, 2}, 3});
send(t, baz{foo{1, 2}, bar{foo{3, 4}, 5}});
{
scoped_actor self;
self->send(t, bar{foo{1, 2}, 3});
self->send(t, baz{foo{1, 2}, bar{foo{3, 4}, 5}});
}
await_all_actors_done();
shutdown();
return 0;
......
......@@ -131,12 +131,12 @@ class tree_type_info : public util::abstract_uniform_type_info<tree> {
typedef std::vector<tree> tree_vector;
// receives `remaining` messages
void testee(size_t remaining) {
void testee(untyped_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(remaining - 1);
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
become (
self->become (
on_arg_match >> [=](const tree& tmsg) {
// prints the tree in its serialized format:
// @<> ( { tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } ) } )
......@@ -188,18 +188,22 @@ int main() {
11 12 13 21 22
*/
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
{ // lifetime scope of self
scoped_actor self;
// send a tree
send(t, t0);
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
// send a vector of trees
announce<tree_vector>();
tree_vector tvec;
tvec.push_back(t0);
tvec.push_back(t0);
send(t, tvec);
// send a tree
self->send(t, t0);
// send a vector of trees
announce<tree_vector>();
tree_vector tvec;
tvec.push_back(t0);
tvec.push_back(t0);
self->send(t, tvec);
}
await_all_actors_done();
shutdown();
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#include <utility>
......@@ -39,6 +39,8 @@ namespace cppa {
actor::actor(abstract_actor* ptr) : m_ops(ptr) { }
actor::actor(const invalid_actor_t&) : m_ops(nullptr) { }
void actor::enqueue(const message_header& hdr, any_tuple msg) const {
if (m_ops.m_ptr) m_ops.m_ptr->enqueue(hdr, std::move(msg));
}
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#include "cppa/actor.hpp"
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#include <utility>
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include "cppa/singletons.hpp"
#include "cppa/blocking_untyped_actor.hpp"
#include "cppa/detail/actor_registry.hpp"
namespace cppa {
void blocking_untyped_actor::await_all_other_actors_done() {
get_actor_registry()->await_running_count_equal(1);
}
} // namespace cppa
......@@ -49,7 +49,8 @@ using std::cout;
using std::endl;
using std::move;
namespace cppa { namespace io {
namespace cppa {
namespace io {
namespace {
......@@ -57,31 +58,20 @@ constexpr size_t default_max_buffer_size = 65535;
} // namespace <anonymous>
class default_broker : public broker {
public:
typedef std::function<void (broker*)> function_type;
template<typename... Ts>
default_broker(function_type&& fun, Ts&&... args)
: broker{std::forward<Ts>(args)...}, m_fun{move(fun)} { }
void init() override {
enqueue({invalid_actor_addr, channel{this}}, make_any_tuple(atom("INITMSG")));
become(
on(atom("INITMSG")) >> [=] {
unbecome();
m_fun(this);
}
);
}
private:
function_type m_fun;
};
default_broker::default_broker(input_stream_ptr in,
output_stream_ptr out,
function_type f)
: broker(std::move(in), std::move(out)), m_fun(std::move(f)) { }
behavior default_broker::make_behavior() {
enqueue({invalid_actor_addr, channel{this}}, make_any_tuple(atom("INITMSG")));
return (
on(atom("INITMSG")) >> [=] {
unbecome();
m_fun(this);
}
);
}
class broker::continuation {
......@@ -299,24 +289,23 @@ void broker::invoke_message(const message_header& hdr, any_tuple msg) {
try {
using detail::receive_policy;
auto bhvr = m_bhvr_stack.back();
switch (m_recv_policy.handle_message(this,
&m_dummy_node,
bhvr,
m_bhvr_stack.back_id(),
receive_policy::sequential{})) {
case receive_policy::hm_msg_handled: {
switch (m_invoke.handle_message(this,
&m_dummy_node,
bhvr,
m_bhvr_stack.back_id())) {
case policy::hm_msg_handled: {
if ( not m_bhvr_stack.empty()
&& bhvr.as_behavior_impl() == m_bhvr_stack.back().as_behavior_impl()) {
m_recv_policy.invoke_from_cache(this, bhvr, m_bhvr_stack.back_id());
m_invoke.invoke_from_cache(this, bhvr, m_bhvr_stack.back_id());
}
break;
}
case receive_policy::hm_drop_msg:
case policy::hm_drop_msg:
break;
case receive_policy::hm_skip_msg:
case receive_policy::hm_cache_msg: {
case policy::hm_skip_msg:
case policy::hm_cache_msg: {
auto e = mailbox_element::create(hdr, move(m_dummy_node.msg));
m_recv_policy.add_to_cache(e);
m_invoke.add_to_cache(e);
break;
}
default: CPPA_CRITICAL("illegal result of handle_message");
......@@ -398,7 +387,7 @@ void broker::write(const connection_handle& hdl, util::buffer&& buf) {
}
local_actor_ptr init_and_launch(broker_ptr ptr) {
ptr->init();
//ptr->init();
// continue reader only if not inherited from default_broker_impl
CPPA_LOGF_WARNING_IF(!ptr->has_behavior(), "broker w/o behavior spawned");
auto mm = get_middleman();
......@@ -421,6 +410,7 @@ local_actor_ptr init_and_launch(broker_ptr ptr) {
return ptr;
}
/*
broker_ptr broker::from_impl(std::function<void (broker*)> fun,
input_stream_ptr in,
output_stream_ptr out) {
......@@ -430,6 +420,7 @@ broker_ptr broker::from_impl(std::function<void (broker*)> fun,
broker_ptr broker::from(std::function<void (broker*)> fun, acceptor_uptr in) {
return make_counted<default_broker>(move(fun), move(in));
}
*/
void broker::erase_io(int id) {
......@@ -454,16 +445,19 @@ accept_handle broker::add_doorman(acceptor_uptr ptr) {
return id;
}
actor_addr broker::fork_impl(std::function<void (broker*)> fun,
connection_handle hdl) {
actor broker::fork_impl(std::function<void (broker*)> fun,
connection_handle hdl) {
auto i = m_io.find(hdl);
if (i == m_io.end()) throw std::invalid_argument("invalid handle");
/*FIXME
scribe* sptr = i->second.get(); // non-owning pointer
auto result = make_counted<default_broker>(move(fun), move(i->second));
init_and_launch(result);
sptr->set_broker(result); // set new broker
m_io.erase(i);
return {result};
*/
return nullptr;
}
void broker::receive_policy(const connection_handle& hdl,
......@@ -473,4 +467,5 @@ void broker::receive_policy(const connection_handle& hdl,
if (i != m_io.end()) i->second->receive_policy(policy, buffer_size);
}
} } // namespace cppa::network
} // namespace io
} // namespace cppa
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#include "cppa/actor_addr.hpp"
......
......@@ -36,12 +36,13 @@
#include "cppa/cppa.hpp"
#include "cppa/self.hpp"
namespace cppa { namespace policy {
namespace cppa {
namespace policy {
void context_switching_resume::trampoline(void* this_ptr) {
auto _this = reinterpret_cast<context_switching_resume*>(this_ptr);
auto _this = reinterpret_cast<blocking_untyped_actor*>(this_ptr);
bool cleanup_called = false;
try { _this->run(); }
try { _this->act(); }
catch (actor_exited&) {
// cleanup already called by scheduled_actor::quit
cleanup_called = true;
......@@ -56,10 +57,21 @@ void context_switching_resume::trampoline(void* this_ptr) {
detail::yield(detail::yield_state::done);
}
} } // namespace cppa::policy
} // namespace policy
} // namespace cppa
#else // ifdef CPPA_DISABLE_CONTEXT_SWITCHING
namespace cppa {
namespace policy {
void context_switching_resume::trampoline(void*) {
throw std::logic_error("context_switching_resume::trampoline called");
}
} // namespace policy
} // namespace cppa
namespace cppa { int keep_compiler_happy_function() { return 42; } }
#endif // ifdef CPPA_DISABLE_CONTEXT_SWITCHING
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include <iostream>
#include "cppa/to_string.hpp"
#include "cppa/cppa.hpp"
#include "cppa/logging.hpp"
#include "cppa/event_based_actor.hpp"
using namespace std;
namespace cppa {
class default_scheduled_actor : public event_based_actor {
typedef event_based_actor super;
public:
typedef std::function<behavior(event_based_actor*)> fun_type;
default_scheduled_actor(fun_type&& fun)
: super(actor_state::ready), m_fun(std::move(fun)) { }
behavior make_behavior() override {
return m_fun(this);
}
scheduled_actor_type impl_type() {
return default_event_based_impl;
}
private:
fun_type m_fun;
};
intrusive_ptr<event_based_actor> event_based_actor::from(std::function<behavior()> fun) {
return make_counted<default_scheduled_actor>([=](event_based_actor*) -> behavior { return fun(); });
}
intrusive_ptr<event_based_actor> event_based_actor::from(std::function<behavior(event_based_actor*)> fun) {
return make_counted<default_scheduled_actor>(std::move(fun));
}
intrusive_ptr<event_based_actor> event_based_actor::from(std::function<void(event_based_actor*)> fun) {
auto result = make_counted<default_scheduled_actor>([=](event_based_actor* self) -> behavior {
return (
on(atom("INIT")) >> [=] {
fun(self);
}
);
});
result->enqueue({result->address(), result}, make_any_tuple(atom("INIT")));
return result;
}
intrusive_ptr<event_based_actor> event_based_actor::from(std::function<void()> fun) {
return from([=](event_based_actor*) { fun(); });
}
event_based_actor::event_based_actor(actor_state st) : super(st, true) { }
resume_result event_based_resume::resume(untyped_actor* self, util::fiber*) {
CPPA_LOG_TRACE("id = " << self->id() << ", state = " << static_cast<int>(state()));
CPPA_REQUIRE( self->state() == actor_state::ready
|| self->state() == actor_state::pending);
auto done_cb = [&]() -> bool {
CPPA_LOG_TRACE("");
if (self->exit_reason() == self->exit_reason::not_exited) {
if (self->planned_exit_reason() == self->exit_reason::not_exited) {
self->planned_exit_reason(exit_reason::normal);
}
self->on_exit();
if (!m_bhvr_stack.empty()) {
planned_exit_reason(exit_reason::not_exited);
return false; // on_exit did set a new behavior
}
cleanup(planned_exit_reason());
}
set_state(actor_state::done);
m_bhvr_stack.clear();
m_bhvr_stack.cleanup();
on_exit();
CPPA_REQUIRE(next_job == nullptr);
return true;
};
CPPA_REQUIRE(next_job == nullptr);
try {
//auto e = m_mailbox.try_pop();
for (auto e = m_mailbox.try_pop(); ; e = m_mailbox.try_pop()) {
//e = m_mailbox.try_pop();
if (e == nullptr) {
CPPA_REQUIRE(next_job == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "no more element in mailbox; going to block");
set_state(actor_state::about_to_block);
std::atomic_thread_fence(std::memory_order_seq_cst);
if (this->m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready:
// interrupted by arriving message
// restore members
CPPA_REQUIRE(m_chained_actor == nullptr);
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"interrupted by arriving message");
break;
case actor_state::blocked:
CPPA_LOGMF(CPPA_DEBUG, self, "set state successfully to blocked");
// done setting actor to blocked
return resume_result::actor_blocked;
default:
CPPA_LOGMF(CPPA_ERROR, self, "invalid state");
CPPA_CRITICAL("invalid state");
};
}
else {
CPPA_LOGMF(CPPA_DEBUG, self, "switched back to ready: "
"mailbox can fetch more");
set_state(actor_state::ready);
CPPA_REQUIRE(m_chained_actor == nullptr);
}
}
else {
CPPA_LOGMF(CPPA_DEBUG, self, "try to invoke message: " << to_string(e->msg));
if (m_bhvr_stack.invoke(m_recv_policy, this, e)) {
CPPA_LOG_DEBUG_IF(m_chained_actor,
"set actor with ID "
<< m_chained_actor->id()
<< " as successor");
if (m_bhvr_stack.empty() && done_cb()) {
CPPA_LOGMF(CPPA_DEBUG, self, "behavior stack empty");
return resume_result::actor_done;
}
m_bhvr_stack.cleanup();
}
}
}
}
catch (actor_exited& what) {
CPPA_LOG_INFO("actor died because of exception: actor_exited, "
"reason = " << what.reason());
if (exit_reason() == exit_reason::not_exited) {
quit(what.reason());
}
}
catch (std::exception& e) {
CPPA_LOG_WARNING("actor died because of exception: "
<< detail::demangle(typeid(e))
<< ", what() = " << e.what());
if (exit_reason() == exit_reason::not_exited) {
quit(exit_reason::unhandled_exception);
}
}
catch (...) {
CPPA_LOG_WARNING("actor died because of an unknown exception");
if (exit_reason() == exit_reason::not_exited) {
quit(exit_reason::unhandled_exception);
}
}
done_cb();
return resume_result::actor_done;
}
scheduled_actor_type event_based_actor::impl_type() {
return event_based_impl;
}
} // namespace cppa
......@@ -28,29 +28,21 @@
\******************************************************************************/
#include <chrono>
#include <memory>
#include <iostream>
#include <algorithm>
#include "cppa/to_string.hpp"
#include "cppa/exception.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/detail/matches.hpp"
#include "cppa/detail/functor_based_actor.hpp"
namespace cppa {
namespace detail {
thread_mapped_actor::thread_mapped_actor() : m_initialized(true) { }
thread_mapped_actor::thread_mapped_actor(std::function<void()> fun)
: m_initialized(false) {
set_behavior(std::move(fun));
void functor_based_actor::create(untyped_actor*, void_fun fun) {
m_make_behavior = [=](untyped_actor* self) -> behavior {
fun(self);
return behavior{};
};
}
bool thread_mapped_actor::initialized() const {
return m_initialized;
behavior functor_based_actor::make_behavior() {
return m_make_behavior(this);
}
} // namespace cppa::detail
} // namespace detail
} // namespace cppa
......@@ -28,25 +28,18 @@
\******************************************************************************/
#include "cppa/detail/scheduled_actor_dummy.hpp"
#include "cppa/detail/functor_based_blocking_actor.hpp"
namespace cppa { namespace detail {
namespace cppa {
namespace detail {
scheduled_actor_dummy::scheduled_actor_dummy()
: scheduled_actor(actor_state::blocked, false) { }
void scheduled_actor_dummy::enqueue(const message_header&, any_tuple) { }
void scheduled_actor_dummy::quit(std::uint32_t) { }
void scheduled_actor_dummy::dequeue(behavior&) { }
void scheduled_actor_dummy::dequeue_response(behavior&, message_id) { }
bool scheduled_actor_dummy::has_behavior() { return false; }
resume_result scheduled_actor_dummy::resume(util::fiber*) {
return resume_result::actor_blocked;
void functor_based_blocking_actor::create(blocking_untyped_actor*, act_fun fun) {
m_act = fun;
}
scheduled_actor_type scheduled_actor_dummy::impl_type() {
return event_based_impl;
void functor_based_blocking_actor::act() {
m_act(this);
}
} } // namespace cppa::detail
} // namespace detail
} // namespace cppa
......@@ -87,7 +87,7 @@ const std::string& group::module_name() const {
return get_module()->name();
}
struct group_nameserver : event_based_actor {
struct group_nameserver : untyped_actor {
behavior make_behavior() override {
return (
on(atom("GET_GROUP"), arg_match) >> [](const std::string& name) {
......
......@@ -42,7 +42,6 @@
#include "cppa/deserializer.hpp"
#include "cppa/untyped_actor.hpp"
#include "cppa/message_header.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/io/middleman.hpp"
......@@ -133,13 +132,13 @@ class local_group : public group {
typedef intrusive_ptr<local_group> local_group_ptr;
class local_broker : public event_based_actor {
class local_broker : public untyped_actor {
public:
local_broker(local_group_ptr g) : m_group(move(g)) { }
behavior make_behavior() {
behavior make_behavior() override {
return (
on(atom("JOIN"), arg_match) >> [=](const actor_addr& other) {
CPPA_LOGC_TRACE("cppa::local_broker", "init$JOIN",
......@@ -256,7 +255,7 @@ class local_group_proxy : public local_group {
typedef intrusive_ptr<local_group_proxy> local_group_proxy_ptr;
class proxy_broker : public event_based_actor {
class proxy_broker : public untyped_actor {
public:
......@@ -496,7 +495,7 @@ class remote_group_module : public group::module {
}
}
}
self->timed_sync_send(chrono::seconds(10), nameserver, atom("GET_GROUP"), name).then (
self->timed_sync_send(nameserver, chrono::seconds(10), atom("GET_GROUP"), name).then (
on(atom("GROUP"), arg_match) >> [&](const group_ptr& g) {
auto gg = dynamic_cast<local_group*>(g.get());
if (gg) {
......
......@@ -77,38 +77,12 @@ constexpr const char* s_default_debug_name = "actor";
} // namespace <anonymous>
local_actor::local_actor(bool sflag)
local_actor::local_actor()
: m_trap_exit(false)
, m_is_scheduled(sflag), m_dummy_node(), m_current_node(&m_dummy_node)
, m_planned_exit_reason(exit_reason::not_exited) {
# ifdef CPPA_DEBUG_MODE
new (&m_debug_name) std::string (std::to_string(m_id) + "@local");
# endif // CPPA_DEBUG_MODE
}
local_actor::~local_actor() {
using std::string;
# ifdef CPPA_DEBUG_MODE
m_debug_name.~string();
# endif // CPPA_DEBUG_MODE
}
const char* local_actor::debug_name() const {
# ifdef CPPA_DEBUG_MODE
return m_debug_name.c_str();
# else // CPPA_DEBUG_MODE
return s_default_debug_name;
# endif // CPPA_DEBUG_MODE
}
, m_dummy_node(), m_current_node(&m_dummy_node)
, m_planned_exit_reason(exit_reason::not_exited) { }
void local_actor::debug_name(std::string str) {
# ifdef CPPA_DEBUG_MODE
m_debug_name = std::move(str);
# else // CPPA_DEBUG_MODE
CPPA_LOG_WARNING("unable to set debug name to " << str
<< " (compiled without debug mode enabled)");
# endif // CPPA_DEBUG_MODE
}
local_actor::~local_actor() { }
void local_actor::monitor(const actor_addr& whom) {
if (!whom) return;
......@@ -125,8 +99,6 @@ void local_actor::demonitor(const actor_addr& whom) {
void local_actor::on_exit() { }
void local_actor::init() { }
void local_actor::join(const group_ptr& what) {
if (what && m_subscriptions.count(what) == 0) {
m_subscriptions.insert(std::make_pair(what, what->subscribe(this)));
......@@ -180,6 +152,10 @@ void local_actor::send_tuple(const channel& dest, any_tuple what) {
//TODO:
}
void local_actor::send_exit(const actor_addr& whom, std::uint32_t reason) {
}
void local_actor::remove_handler(message_id) {
}
......@@ -194,6 +170,14 @@ message_future local_actor::timed_sync_send_tuple(const util::duration& rtime,
}
void local_actor::send_tuple(message_priority prio, const channel& dest, any_tuple what) {
}
message_future local_actor::sync_send_tuple(const actor& dest, any_tuple what) {
}
response_handle local_actor::make_response_handle() {
return {};
}
......
......@@ -103,7 +103,7 @@ class logging_impl : public logging {
const char* function_name,
const char* c_full_file_name,
int line_num,
actor_addr from,
actor_addr,
const std::string& msg) {
string class_name = c_class_name;
replace_all(class_name, "::", ".");
......
......@@ -46,7 +46,6 @@
#include "cppa/actor_proxy.hpp"
#include "cppa/binary_serializer.hpp"
#include "cppa/uniform_type_info.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/binary_deserializer.hpp"
#include "cppa/util/buffer.hpp"
......@@ -347,9 +346,6 @@ class middleman_overseer : public continuable {
middleman::~middleman() { }
void middleman_loop(middleman_impl* impl) {
# ifdef CPPA_LOG_LEVEL
auto mself = make_counted<thread_mapped_actor>();
# endif
middleman_event_handler* handler = impl->m_handler.get();
CPPA_LOGF_TRACE("run middleman loop");
CPPA_LOGF_INFO("middleman runs at "
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include "cppa/resumable.hpp"
namespace cppa {
resumable::~resumable() { }
} // namespace cppa
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include "cppa/on.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/detail/sync_request_bouncer.hpp"
namespace cppa {
scheduled_actor::scheduled_actor(actor_state init_state, bool chained_send)
: super(chained_send), next(nullptr), m_state(init_state)
, m_scheduler(nullptr), m_hidden(false) { }
void scheduled_actor::attach_to_scheduler(scheduler* sched, bool hidden) {
CPPA_REQUIRE(sched != nullptr);
m_scheduler = sched;
m_hidden = hidden;
// initialize this actor
try { init(); }
catch (...) { }
}
bool scheduled_actor::initialized() const {
return m_scheduler != nullptr;
}
scheduled_actor::~scheduled_actor() {
if (!m_mailbox.closed()) {
detail::sync_request_bouncer f{exit_reason()};
m_mailbox.close(f);
}
}
void scheduled_actor::run_detached() {
throw std::logic_error("scheduled_actor::run_detached called");
}
void scheduled_actor::cleanup(std::uint32_t reason) {
detail::sync_request_bouncer f{reason};
m_mailbox.close(f);
super::cleanup(reason);
}
bool scheduled_actor::enqueue_impl(actor_state next_state,
const message_header& hdr,
any_tuple&& msg) {
CPPA_REQUIRE( next_state == actor_state::ready
|| next_state == actor_state::pending);
auto e = new_mailbox_element(hdr, std::move(msg));
switch (m_mailbox.enqueue(e)) {
case intrusive::first_enqueued: {
auto state = m_state.load();
for (;;) {
switch (state) {
case actor_state::blocked: {
if (m_state.compare_exchange_weak(state, next_state)) {
CPPA_REQUIRE(m_scheduler != nullptr);
if (next_state == actor_state::ready) {
CPPA_LOGMF(CPPA_DEBUG, self, "enqueued actor with id " << id()
<< " to job queue");
m_scheduler->enqueue(this);
return false;
}
return true;
}
break;
}
case actor_state::about_to_block: {
if (m_state.compare_exchange_weak(state, actor_state::ready)) {
return false;
}
break;
}
default: return false;
}
}
break;
}
case intrusive::queue_closed: {
if (hdr.id.is_request()) {
detail::sync_request_bouncer f{exit_reason()};
f(hdr.sender, hdr.id);
}
break;
}
default: break;
}
return false;
}
actor_state scheduled_actor::compare_exchange_state(actor_state expected,
actor_state desired) {
auto e = expected;
do { if (m_state.compare_exchange_weak(e, desired)) return desired; }
while (e == expected);
return e;
}
void scheduled_actor::enqueue(const message_header& hdr, any_tuple msg) {
enqueue_impl(actor_state::ready, hdr, std::move(msg));
}
} // namespace cppa
......@@ -39,8 +39,7 @@
#include "cppa/to_string.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/context_switching_actor.hpp"
#include "cppa/scoped_actor.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/singleton_manager.hpp"
......@@ -90,38 +89,31 @@ class scheduler_helper {
public:
typedef intrusive_ptr<thread_mapped_actor> ptr_type;
void start() {
auto mt = make_counted<thread_mapped_actor>();
auto mp = make_counted<thread_mapped_actor>();
// launch threads
m_timer_thread = std::thread{&scheduler_helper::timer_loop, mt.get()};
m_printer_thread = std::thread{&scheduler_helper::printer_loop, mp.get()};
// set member variables
m_timer = std::move(mt);
m_printer = std::move(mp);
m_timer_thread = std::thread{&scheduler_helper::timer_loop, m_timer.get()};
m_printer_thread = std::thread{&scheduler_helper::printer_loop, m_printer.get()};
}
void stop() {
auto msg = make_any_tuple(atom("DIE"));
m_timer.enqueue({invalid_actor_addr, nullptr}, msg);
m_printer.enqueue({invalid_actor_addr, nullptr}, msg);
m_timer->enqueue({invalid_actor_addr, nullptr}, msg);
m_printer->enqueue({invalid_actor_addr, nullptr}, msg);
m_timer_thread.join();
m_printer_thread.join();
}
actor m_timer;
scoped_actor m_timer;
std::thread m_timer_thread;
actor m_printer;
scoped_actor m_printer;
std::thread m_printer_thread;
private:
static void timer_loop(thread_mapped_actor* self);
static void timer_loop(blocking_untyped_actor* self);
static void printer_loop(thread_mapped_actor* self);
static void printer_loop(blocking_untyped_actor* self);
};
......@@ -136,7 +128,7 @@ inline void insert_dmsg(Map& storage,
storage.insert(std::make_pair(std::move(tout), std::move(dmsg)));
}
void scheduler_helper::timer_loop(thread_mapped_actor* self) {
void scheduler_helper::timer_loop(blocking_untyped_actor* self) {
// setup & local variables
bool done = false;
std::unique_ptr<mailbox_element, detail::disposer> msg_ptr;
......@@ -163,7 +155,7 @@ void scheduler_helper::timer_loop(thread_mapped_actor* self) {
// loop
while (!done) {
while (!msg_ptr) {
if (messages.empty()) msg_ptr.reset(self->pop());
if (messages.empty()) msg_ptr.reset(self->dequeue());
else {
tout = hrc::now();
// handle timeouts (send messages)
......@@ -175,7 +167,7 @@ void scheduler_helper::timer_loop(thread_mapped_actor* self) {
}
// wait for next message or next timeout
if (it != messages.end()) {
msg_ptr.reset(self->try_pop(it->first));
msg_ptr.reset(self->try_dequeue(it->first));
}
}
}
......@@ -184,7 +176,7 @@ void scheduler_helper::timer_loop(thread_mapped_actor* self) {
}
}
void scheduler_helper::printer_loop(thread_mapped_actor* self) {
void scheduler_helper::printer_loop(blocking_untyped_actor* self) {
std::map<actor_addr, std::string> out;
auto flush_output = [&out](const actor_addr& s) {
auto i = out.find(s);
......@@ -257,8 +249,8 @@ scheduler::~scheduler() {
delete m_helper;
}
actor& scheduler::delayed_send_helper() {
return m_helper->m_timer;
actor scheduler::delayed_send_helper() {
return m_helper->m_timer.get();
}
void scheduler::register_converted_context(abstract_actor* what) {
......@@ -287,8 +279,8 @@ scheduler* scheduler::create_singleton() {
return new detail::thread_pool_scheduler;
}
const actor& scheduler::printer() const {
return m_helper->m_printer;
actor scheduler::printer() const {
return m_helper->m_printer.get();
}
} // namespace cppa
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include "cppa/policy.hpp"
#include "cppa/scoped_actor.hpp"
#include "cppa/detail/proper_actor.hpp"
namespace cppa {
namespace {
struct impl : blocking_untyped_actor {
void act() override { }
};
blocking_untyped_actor* alloc() {
using namespace policy;
return new detail::proper_actor<impl,
no_scheduling,
not_prioritizing,
no_resume,
nestable_invoke>;
}
} // namespace <anonymous>
scoped_actor::scoped_actor() : m_self(alloc()) { }
scoped_actor::~scoped_actor() { }
} // namespace cppa
......@@ -36,7 +36,6 @@
#include "cppa/exception.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/io/middleman.hpp"
......
......@@ -25,7 +25,7 @@
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
\******************************************************************************/
#include "cppa/atom.hpp"
......
......@@ -36,12 +36,8 @@
#include "cppa/on.hpp"
#include "cppa/logging.hpp"
#include "cppa/prioritizing.hpp"
#include "cppa/untyped_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/blocking_untyped_actor.hpp"
#include "cppa/context_switching_actor.hpp"
#include "cppa/util/fiber.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/thread_pool_scheduler.hpp"
......@@ -51,9 +47,13 @@ using std::endl;
namespace cppa { namespace detail {
resumable::resume_result thread_pool_scheduler::dummy::resume(util::fiber*) {
throw std::logic_error("thread_pool_scheduler::dummy::resume");
}
struct thread_pool_scheduler::worker {
typedef scheduled_actor* job_ptr;
typedef resumable* job_ptr;
job_queue* m_job_queue;
job_ptr m_dummy;
......@@ -109,11 +109,11 @@ struct thread_pool_scheduler::worker {
return; // and say goodbye
}
CPPA_LOG_DEBUG("resume actor with ID " << job->id());
if (job->resume(&fself) == resume_result::actor_done) {
if (job->resume(&fself) == resumable::done) {
CPPA_LOGMF(CPPA_DEBUG, self, "actor is done");
bool hidden = job->is_hidden();
/*FIXME bool hidden = job->is_hidden();
job->deref();
if (!hidden) get_actor_registry()->dec_running();
if (!hidden)*/ get_actor_registry()->dec_running();
}
job = nullptr;
}
......@@ -134,7 +134,7 @@ thread_pool_scheduler::thread_pool_scheduler(size_t num_worker_threads) {
}
void thread_pool_scheduler::supervisor_loop(job_queue* jqueue,
scheduled_actor* dummy,
resumable* dummy,
size_t num_threads) {
std::vector<std::unique_ptr<thread_pool_scheduler::worker> > workers;
for (size_t i = 0; i < num_threads; ++i) {
......@@ -164,116 +164,18 @@ void thread_pool_scheduler::destroy() {
auto ptr = m_queue.try_pop();
while (ptr != nullptr) {
if (ptr != &m_dummy) {
bool hidden = ptr->is_hidden();
/*FIXME bool hidden = ptr->is_hidden();
ptr->deref();
std::atomic_thread_fence(std::memory_order_seq_cst);
if (!hidden) get_actor_registry()->dec_running();
if (!hidden)*/ get_actor_registry()->dec_running();
}
ptr = m_queue.try_pop();
}
super::destroy();
}
void thread_pool_scheduler::enqueue(scheduled_actor* what) {
void thread_pool_scheduler::enqueue(resumable* what) {
m_queue.push_back(what);
}
void exec_as_thread(bool is_hidden, intrusive_ptr<untyped_actor> ptr) {
/*TODO:
if (!is_hidden) get_actor_registry()->inc_running();
std::thread([=] {
try { ptr->exec_bhvr_stack(); }
catch (...) { }
if (!is_hidden) {
std::atomic_thread_fence(std::memory_order_seq_cst);
get_actor_registry()->dec_running();
}
}).detach();
*/
}
local_actor_ptr thread_pool_scheduler::exec(spawn_options os, scheduled_actor_ptr p) {
/*TODO:
CPPA_REQUIRE(p != nullptr);
bool is_hidden = has_hide_flag(os);
if (has_detach_flag(os)) {
exec_as_thread(is_hidden, p [p] {
p->run_detached();
});
return p;
}
p->attach_to_scheduler(this, is_hidden);
if (p->has_behavior() || p->impl_type() == default_event_based_impl) {
if (!is_hidden) get_actor_registry()->inc_running();
p->ref(); // implicit reference that's released if actor dies
if (p->impl_type() != event_based_impl) m_queue.push_back(p.get());
}
else p->on_exit();
return p;
*/
}
local_actor_ptr thread_pool_scheduler::exec(spawn_options os,
init_callback cb,
actor_fun f) {
local_actor_ptr result;
auto set_result = [&](local_actor_ptr value) {
CPPA_REQUIRE(result == nullptr && value != nullptr);
result = std::move(value);
if (cb) cb(result.get());
};
if (has_priority_aware_flag(os)) {
using impl = extend<local_actor>::with<stackless, threaded, prioritizing>;
set_result(make_counted<impl>());
/*TODO:
exec_as_thread(has_hide_flag(os), [result, f] {
try {
f();
}
catch (actor_exited& e) { }
catch (std::exception& e) {
CPPA_LOGF_ERROR("actor with ID " << result->id()
<< " terminated due to an unhandled exception; "
<< detail::demangle(typeid(e)) << ": "
<< e.what());
}
catch (...) {
CPPA_LOGF_ERROR("actor with ID " << result->id()
<< " terminated due to an unknown exception");
}
result->on_exit();
});
*/
}
else if (has_blocking_api_flag(os)) {
# ifndef CPPA_DISABLE_CONTEXT_SWITCHING
if (!has_detach_flag(os)) {
auto p = make_counted<context_switching_actor>(std::move(f));
set_result(p);
exec(os, std::move(p));
}
else
# endif
/* else tree */ {
//auto p = make_counted<thread_mapped_actor>(std::move(f));
//set_result(p);
/*TODO:
exec_as_thread(has_hide_flag(os), [p] {
p->run();
p->on_exit();
});
*/
}
}
else {
/*TODO:
auto p = event_based_actor::from(std::move(f));
set_result(p);
exec(os, p);
*/
}
CPPA_REQUIRE(result != nullptr);
return result;
}
} } // namespace cppa::detail
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include "cppa/untyped_actor.hpp"
namespace cppa {
void untyped_actor::forward_to(const actor&) {
}
} // namespace cppa
......@@ -15,21 +15,21 @@ namespace {
size_t s_pongs = 0;
behavior ping_behavior(size_t num_pings) {
behavior ping_behavior(local_actor* self, size_t num_pings) {
return (
on(atom("pong"), arg_match) >> [num_pings](int value) -> any_tuple {
on(atom("pong"), arg_match) >> [=](int value) -> any_tuple {
CPPA_LOGF_ERROR_IF(!self->last_sender(), "last_sender() == nullptr");
CPPA_LOGF_INFO("received {'pong', " << value << "}");
//cout << to_string(self->last_dequeued()) << endl;
if (++s_pongs >= num_pings) {
CPPA_LOGF_INFO("reached maximum, send {'EXIT', user_defined} "
<< "to last sender and quit with normal reason");
send_exit(self->last_sender(), exit_reason::user_shutdown);
self->send_exit(self->last_sender(), exit_reason::user_shutdown);
self->quit();
}
return {atom("ping"), value};
return make_any_tuple(atom("ping"), value);
},
others() >> [] {
others() >> [=] {
CPPA_LOGF_ERROR("unexpected message; "
<< to_string(self->last_dequeued()));
self->quit(exit_reason::user_shutdown);
......@@ -37,13 +37,13 @@ behavior ping_behavior(size_t num_pings) {
);
}
behavior pong_behavior() {
behavior pong_behavior(local_actor* self) {
return (
on(atom("ping"), arg_match) >> [](int value) -> any_tuple {
CPPA_LOGF_INFO("received {'ping', " << value << "}");
return {atom("pong"), value + 1};
return make_any_tuple(atom("pong"), value + 1);
},
others() >> [] {
others() >> [=] {
CPPA_LOGF_ERROR("unexpected message; "
<< to_string(self->last_dequeued()));
self->quit(exit_reason::user_shutdown);
......@@ -57,27 +57,27 @@ size_t pongs() {
return s_pongs;
}
void ping(size_t num_pings) {
void ping(blocking_untyped_actor* self, size_t num_pings) {
CPPA_LOGF_TRACE("num_pings = " << num_pings);
s_pongs = 0;
receive_loop(ping_behavior(num_pings));
self->receive_loop(ping_behavior(self, num_pings));
}
void event_based_ping(size_t num_pings) {
void event_based_ping(untyped_actor* self, size_t num_pings) {
CPPA_LOGF_TRACE("num_pings = " << num_pings);
s_pongs = 0;
become(ping_behavior(num_pings));
self->become(ping_behavior(self, num_pings));
}
void pong(actor_ptr ping_actor) {
void pong(blocking_untyped_actor* self, actor ping_actor) {
CPPA_LOGF_TRACE("ping_actor = " << to_string(ping_actor));
send(ping_actor, atom("pong"), 0); // kickoff
receive_loop(pong_behavior());
self->send(ping_actor, atom("pong"), 0); // kickoff
self->receive_loop(pong_behavior(self));
}
void event_based_pong(actor_ptr ping_actor) {
void event_based_pong(untyped_actor* self, actor ping_actor) {
CPPA_LOGF_TRACE("ping_actor = " << to_string(ping_actor));
CPPA_REQUIRE(ping_actor != nullptr);
send(ping_actor, atom("pong"), 0); // kickoff
become(pong_behavior());
self->send(ping_actor, atom("pong"), 0); // kickoff
self->become(pong_behavior(self));
}
......@@ -10,9 +10,9 @@ void ping(cppa::blocking_untyped_actor*, size_t num_pings);
void event_based_ping(cppa::untyped_actor*, size_t num_pings);
void pong(cppa::blocking_untyped_actor*, cppa::actor_ptr ping_actor);
void pong(cppa::blocking_untyped_actor*, cppa::actor ping_actor);
void event_based_pong(cppa::untyped_actor*, cppa::actor_ptr ping_actor);
void event_based_pong(cppa::untyped_actor*, cppa::actor ping_actor);
// returns the number of messages ping received
size_t pongs();
......
......@@ -60,6 +60,10 @@ inline std::string cppa_stream_arg(const cppa::actor& ptr) {
return cppa::to_string(ptr);
}
inline std::string cppa_stream_arg(const cppa::actor_addr& ptr) {
return cppa::to_string(ptr);
}
inline std::string cppa_stream_arg(const bool& value) {
return value ? "true" : "false";
}
......
......@@ -5,6 +5,7 @@
#include "test.hpp"
#include "cppa/cppa.hpp"
#include "cppa/scoped_actor.hpp"
namespace cppa {
inline std::ostream& operator<<(std::ostream& out, const atom_value& a) {
......@@ -27,6 +28,15 @@ void foo() {
<< " = " << to_string(AtomValue) << ")");
}
struct mirror {
mirror(local_actor* self) : m_self(self) { }
template<typename... Ts>
void operator()(Ts&&... args) {
m_self->send(m_self, make_any_tuple(std::forward<Ts>(args)...));
}
local_actor* m_self;
};
int main() {
bool matched_pattern[3] = { false, false, false };
CPPA_TEST(test_atom);
......@@ -38,12 +48,14 @@ int main() {
CPPA_CHECK_EQUAL(atom(" "), atom("@!?"));
// check to_string impl.
CPPA_CHECK_EQUAL(to_string(s_foo), "FooBar");
self << make_cow_tuple(atom("foo"), static_cast<std::uint32_t>(42))
<< make_cow_tuple(atom(":Attach"), atom(":Baz"), "cstring")
<< make_cow_tuple(atom("b"), atom("a"), atom("c"), 23.f)
<< make_cow_tuple(atom("a"), atom("b"), atom("c"), 23.f);
scoped_actor self;
mirror m(self.get());
m(atom("foo"), static_cast<std::uint32_t>(42));
m(atom(":Attach"), atom(":Baz"), "cstring");
m(atom("b"), atom("a"), atom("c"), 23.f);
m(atom("a"), atom("b"), atom("c"), 23.f);
int i = 0;
receive_for(i, 3) (
self->receive_for(i, 3) (
on<atom("foo"), std::uint32_t>() >> [&](std::uint32_t value) {
matched_pattern[0] = true;
CPPA_CHECK_EQUAL(value, 42);
......@@ -58,7 +70,7 @@ int main() {
}
);
CPPA_CHECK(matched_pattern[0] && matched_pattern[1] && matched_pattern[2]);
receive (
self->receive (
// "erase" message { atom("b"), atom("a"), atom("c"), 23.f }
others() >> CPPA_CHECKPOINT_CB(),
after(std::chrono::seconds(0)) >> CPPA_UNEXPECTED_TOUT_CB()
......
......@@ -135,7 +135,8 @@ int main(int argc, char** argv) {
CPPA_CHECKPOINT();
auto p = spawn(ping, 10);
CPPA_CHECKPOINT();
auto cl = spawn_io(peer, "localhost", port, p);
//FIXME auto cl = spawn_io(peer, "localhost", port, p);
actor cl;
CPPA_CHECKPOINT();
send_as(nullptr, p, atom("kickoff"), cl);
CPPA_CHECKPOINT();
......@@ -154,7 +155,7 @@ int main(int argc, char** argv) {
uint16_t port = 4242;
for (;;) {
try {
spawn_io_server(peer_acceptor, port, p);
//FIXME: spawn_io_server(peer_acceptor, port, p);
CPPA_CHECKPOINT();
ostringstream oss;
oss << app_path << " mode=client port=" << port << " &>/dev/null";
......
......@@ -9,7 +9,6 @@
#include "test.hpp"
#include "cppa/on.hpp"
#include "cppa/self.hpp"
#include "cppa/cppa.hpp"
#include "cppa/actor.hpp"
#include "cppa/group.hpp"
......@@ -22,14 +21,14 @@ using std::endl;
using std::string;
using namespace cppa;
void testee(int current_value, int final_result) {
become(
void testee(untyped_actor* self, int current_value, int final_result) {
self->become(
on_arg_match >> [=](int result) {
auto next = result + current_value;
if (next >= final_result) self->quit();
else testee(next, final_result);
else testee(self, next, final_result);
},
after(std::chrono::seconds(2)) >> [] {
after(std::chrono::seconds(2)) >> [=] {
CPPA_UNEXPECTED_TOUT();
self->quit(exit_reason::user_shutdown);
}
......@@ -38,8 +37,9 @@ void testee(int current_value, int final_result) {
int main() {
CPPA_TEST(test_local_group);
/*
auto foo_group = group::get("local", "foo");
actor_ptr master = spawn_in_group(foo_group, testee, 0, 10);
auto master = spawn_in_group(foo_group, testee, 0, 10);
for (int i = 0; i < 5; ++i) {
// spawn five workers and let them join local/foo
spawn_in_group(foo_group, [master] {
......@@ -52,5 +52,6 @@ int main() {
send(foo_group, 2);
await_all_actors_done();
shutdown();
*/
return CPPA_TEST_RESULT();
}
......@@ -18,10 +18,10 @@ namespace {
typedef std::pair<std::string, std::string> string_pair;
typedef vector<actor_ptr> actor_vector;
typedef vector<actor> actor_vector;
void reflector() {
become (
void reflector(untyped_actor* self) {
self->become (
others() >> [=] {
CPPA_LOGF_INFO("reflect and quit");
self->quit();
......@@ -30,26 +30,26 @@ void reflector() {
);
}
void spawn5_server_impl(actor_ptr client, group_ptr grp) {
void spawn5_server_impl(untyped_actor* self, actor client, group_ptr grp) {
CPPA_LOGF_TRACE(CPPA_TARG(client, to_string)
<< ", " << CPPA_TARG(grp, to_string));
spawn_in_group(grp, reflector);
spawn_in_group(grp, reflector);
//FIXME spawn_in_group(grp, reflector);
//FIXME spawn_in_group(grp, reflector);
CPPA_LOGF_INFO("send {'Spawn5'} and await {'ok', actor_vector}");
sync_send(client, atom("Spawn5"), grp).then(
self->sync_send(client, atom("Spawn5"), grp).then(
on(atom("ok"), arg_match) >> [=](const actor_vector& vec) {
CPPA_LOGF_INFO("received vector with " << vec.size() << " elements");
send(grp, "Hello reflectors!", 5.0);
self->send(grp, "Hello reflectors!", 5.0);
if (vec.size() != 5) {
CPPA_PRINTERR("remote client did not spawn five reflectors!");
}
for (auto& a : vec) self->monitor(a);
},
others() >> [] {
others() >> [=] {
CPPA_UNEXPECTED_MSG();
self->quit(exit_reason::unhandled_exception);
},
after(chrono::seconds(10)) >> [] {
after(chrono::seconds(10)) >> [=] {
CPPA_UNEXPECTED_TOUT();
self->quit(exit_reason::unhandled_exception);
}
......@@ -58,34 +58,34 @@ void spawn5_server_impl(actor_ptr client, group_ptr grp) {
CPPA_PRINT("wait for reflected messages");
// receive seven reply messages (2 local, 5 remote)
auto replies = std::make_shared<int>(0);
become (
self->become (
on("Hello reflectors!", 5.0) >> [=] {
if (++*replies == 7) {
CPPA_PRINT("wait for DOWN messages");
auto downs = std::make_shared<int>(0);
become (
self->become (
on(atom("DOWN"), arg_match) >> [=](std::uint32_t reason) {
if (reason != exit_reason::normal) {
CPPA_PRINTERR("reflector exited for non-normal exit reason!");
}
if (++*downs == 5) {
CPPA_CHECKPOINT();
send(client, atom("Spawn5Done"));
self->send(client, atom("Spawn5Done"));
self->quit();
}
},
others() >> [] {
others() >> [=] {
CPPA_UNEXPECTED_MSG();
self->quit(exit_reason::unhandled_exception);
},
after(chrono::seconds(2)) >> [] {
after(chrono::seconds(2)) >> [=] {
CPPA_UNEXPECTED_TOUT();
self->quit(exit_reason::unhandled_exception);
}
);
}
},
after(std::chrono::seconds(2)) >> [] {
after(std::chrono::seconds(2)) >> [=] {
CPPA_UNEXPECTED_TOUT();
self->quit(exit_reason::unhandled_exception);
}
......@@ -94,33 +94,33 @@ void spawn5_server_impl(actor_ptr client, group_ptr grp) {
}
// receive seven reply messages (2 local, 5 remote)
void spawn5_server(actor_ptr client, bool inverted) {
if (!inverted) spawn5_server_impl(client, group::get("local", "foobar"));
void spawn5_server(untyped_actor* self, actor client, bool inverted) {
if (!inverted) spawn5_server_impl(self, client, group::get("local", "foobar"));
else {
CPPA_LOGF_INFO("request group");
sync_send(client, atom("GetGroup")).then (
self->sync_send(client, atom("GetGroup")).then (
[=](const group_ptr& remote_group) {
spawn5_server_impl(client, remote_group);
spawn5_server_impl(self, client, remote_group);
}
);
}
}
void spawn5_client() {
become (
void spawn5_client(untyped_actor* self) {
self->become (
on(atom("GetGroup")) >> []() -> group_ptr {
CPPA_LOGF_INFO("received {'GetGroup'}");
return group::get("local", "foobar");
},
on(atom("Spawn5"), arg_match) >> [=](const group_ptr& grp) -> any_tuple {
on(atom("Spawn5"), arg_match) >> [=](const group_ptr&) -> any_tuple {
CPPA_LOGF_INFO("received {'Spawn5'}");
actor_vector vec;
for (int i = 0; i < 5; ++i) {
vec.push_back(spawn_in_group(grp, reflector));
//FIXME vec.push_back(spawn_in_group(grp, reflector));
}
return {atom("ok"), std::move(vec)};
return make_any_tuple(atom("ok"), std::move(vec));
},
on(atom("Spawn5Done")) >> [] {
on(atom("Spawn5Done")) >> [=] {
CPPA_LOGF_INFO("received {'Spawn5Done'}");
self->quit();
}
......@@ -129,8 +129,9 @@ void spawn5_client() {
} // namespace <anonymous>
template<typename T>
void await_down(actor_ptr ptr, T continuation) {
template<typename F>
void await_down(actor, F) {
/*
become (
on(atom("DOWN"), arg_match) >> [=](uint32_t) -> bool {
if (self->last_sender() == ptr) {
......@@ -140,27 +141,28 @@ void await_down(actor_ptr ptr, T continuation) {
return false; // not the 'DOWN' message we are waiting for
}
);
*/
}
static constexpr size_t num_pings = 10;
class client : public event_based_actor {
class client : public untyped_actor {
public:
client(actor_ptr server) : m_server(std::move(server)) { }
client(actor server) : m_server(std::move(server)) { }
void init() {
spawn_ping();
behavior make_behavior() override {
return spawn_ping();
}
private:
void spawn_ping() {
behavior spawn_ping() {
CPPA_PRINT("send {'SpawnPing'}");
send(m_server, atom("SpawnPing"));
become (
on(atom("PingPtr"), arg_match) >> [=](const actor_ptr& ping) {
return (
on(atom("PingPtr"), arg_match) >> [=](const actor& ping) {
auto pptr = spawn<monitored+detached+blocking_api>(pong, ping);
await_down(pptr, [=] {
send_sync_msg();
......@@ -195,7 +197,7 @@ class client : public event_based_actor {
void test_group_comm() {
CPPA_PRINT("test group communication via network");
sync_send(m_server, atom("GClient")).then(
on(atom("GClient"), arg_match) >> [=](actor_ptr gclient) {
on(atom("GClient"), arg_match) >> [=](actor gclient) {
auto s5a = spawn<monitored>(spawn5_server, gclient, false);
await_down(s5a, [=]{
test_group_comm_inverted();
......@@ -208,38 +210,38 @@ class client : public event_based_actor {
CPPA_PRINT("test group communication via network (inverted setup)");
become (
on(atom("GClient")) >> [=]() -> any_tuple {
auto cptr = self->last_sender();
auto cptr = last_sender();
auto s5c = spawn<monitored>(spawn5_client);
// set next behavior
await_down(s5c, [=] {
CPPA_CHECKPOINT();
self->quit();
quit();
});
return {atom("GClient"), s5c};
return make_any_tuple(atom("GClient"), s5c);
}
);
}
actor_ptr m_server;
actor m_server;
};
class server : public event_based_actor {
class server : public untyped_actor {
public:
void init() {
await_spawn_ping();
behavior make_behavior() override {
return await_spawn_ping();
}
private:
void await_spawn_ping() {
behavior await_spawn_ping() {
CPPA_PRINT("await {'SpawnPing'}");
become (
return (
on(atom("SpawnPing")) >> [=]() -> any_tuple {
CPPA_PRINT("received {'SpawnPing'}");
auto client = self->last_sender();
auto client = last_sender();
CPPA_LOGF_ERROR_IF(!client, "last_sender() == nullptr");
CPPA_LOGF_INFO("spawn event-based ping actor");
auto pptr = spawn<monitored>(event_based_ping, num_pings);
......@@ -248,7 +250,7 @@ class server : public event_based_actor {
CPPA_CHECK_EQUAL(pongs(), num_pings);
await_sync_msg();
});
return {atom("PingPtr"), pptr};
return make_any_tuple(atom("PingPtr"), pptr);
}
);
}
......@@ -275,7 +277,7 @@ class server : public event_based_actor {
CPPA_CHECK_EQUAL(*foobars, 100);
test_group_comm();
}
return self->last_dequeued();
return last_dequeued();
}
);
}
......@@ -284,23 +286,23 @@ class server : public event_based_actor {
CPPA_PRINT("test group communication via network");
become (
on(atom("GClient")) >> [=]() -> any_tuple {
auto cptr = self->last_sender();
auto cptr = last_sender();
auto s5c = spawn<monitored>(spawn5_client);
await_down(s5c, [=] {
test_group_comm_inverted(cptr);
//test_group_comm_inverted(cptr);
});
return {atom("GClient"), s5c};
return make_any_tuple(atom("GClient"), s5c);
}
);
}
void test_group_comm_inverted(actor_ptr cptr) {
void test_group_comm_inverted(actor cptr) {
CPPA_PRINT("test group communication via network (inverted setup)");
sync_send(cptr, atom("GClient")).then (
on(atom("GClient"), arg_match) >> [=](actor_ptr gclient) {
on(atom("GClient"), arg_match) >> [=](actor gclient) {
await_down(spawn<monitored>(spawn5_server, gclient, true), [=] {
CPPA_CHECKPOINT();
self->quit();
quit();
});
}
);
......@@ -321,6 +323,7 @@ int main(int argc, char** argv) {
}
else {
run_client_part(get_kv_pairs(argc, argv), [](uint16_t port) {
scoped_actor self;
auto serv = remote_actor("localhost", port);
// remote_actor is supposed to return the same server
// when connecting to the same host again
......@@ -331,8 +334,8 @@ int main(int argc, char** argv) {
auto server3 = remote_actor(localhost, port);
CPPA_CHECK(serv == server3);
}
auto c = spawn<client, monitored>(serv);
receive (
auto c = self->spawn<client, monitored>(serv);
self->receive (
on(atom("DOWN"), arg_match) >> [=](uint32_t rsn) {
CPPA_CHECK_EQUAL(self->last_sender(), c);
CPPA_CHECK_EQUAL(rsn, exit_reason::normal);
......@@ -343,44 +346,47 @@ int main(int argc, char** argv) {
}
}
CPPA_TEST(test_remote_actor);
auto serv = spawn<server, monitored>();
uint16_t port = 4242;
bool success = false;
do {
try {
publish(serv, port, "127.0.0.1");
success = true;
CPPA_LOGF_DEBUG("running on port " << port);
}
catch (bind_failure&) {
// try next port
++port;
}
}
while (!success);
thread child;
ostringstream oss;
if (run_remote_actor) {
oss << app_path << " run=remote_actor port=" << port << " &>/dev/null";
// execute client_part() in a separate process,
// connected via localhost socket
child = thread([&oss]() {
CPPA_LOGC_TRACE("NONE", "main$thread_launcher", "");
string cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) {
CPPA_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!");
abort();
{ // lifetime scope of self
scoped_actor self;
auto serv = self->spawn<server, monitored>();
uint16_t port = 4242;
bool success = false;
do {
try {
publish(serv, port, "127.0.0.1");
success = true;
CPPA_LOGF_DEBUG("running on port " << port);
}
catch (bind_failure&) {
// try next port
++port;
}
});
}
else { CPPA_PRINT("actor published at port " << port); }
CPPA_CHECKPOINT();
receive (
on(atom("DOWN"), arg_match) >> [=](uint32_t rsn) {
CPPA_CHECK_EQUAL(self->last_sender(), serv);
CPPA_CHECK_EQUAL(rsn, exit_reason::normal);
}
);
while (!success);
ostringstream oss;
if (run_remote_actor) {
oss << app_path << " run=remote_actor port=" << port << " &>/dev/null";
// execute client_part() in a separate process,
// connected via localhost socket
child = thread([&oss]() {
CPPA_LOGC_TRACE("NONE", "main$thread_launcher", "");
string cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) {
CPPA_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!");
abort();
}
});
}
else { CPPA_PRINT("actor published at port " << port); }
CPPA_CHECKPOINT();
self->receive (
on(atom("DOWN"), arg_match) >> [&](uint32_t rsn) {
CPPA_CHECK_EQUAL(self->last_sender(), serv);
CPPA_CHECK_EQUAL(rsn, exit_reason::normal);
}
);
} // lifetime scope of self
// wait until separate process (in sep. thread) finished execution
await_all_actors_done();
CPPA_CHECKPOINT();
......
......@@ -26,7 +26,6 @@
#include "test.hpp"
#include "cppa/self.hpp"
#include "cppa/cow_tuple.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/announce.hpp"
......@@ -227,7 +226,8 @@ int main() {
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
try {
auto ttup = make_any_tuple(1, 2, actor_ptr(self));
scoped_actor self;
auto ttup = make_any_tuple(1, 2, actor{self.get()});
util::buffer wr_buf;
binary_serializer bs(&wr_buf, &addressing);
bs << ttup;
......@@ -239,7 +239,8 @@ int main() {
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
try {
auto ttup = make_any_tuple(1, 2, actor_ptr(self));
scoped_actor self;
auto ttup = make_any_tuple(1, 2, actor{self.get()});
util::buffer wr_buf;
binary_serializer bs(&wr_buf, &addressing);
bs << ttup;
......@@ -275,47 +276,6 @@ int main() {
}
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
try {
/*
any_tuple msg1 = cppa::make_cow_tuple(42, string("Hello \"World\"!"));
auto msg1_tostring = to_string(msg1);
if (msg1str != msg1_tostring) {
CPPA_FAILURE("msg1str != to_string(msg1)");
cerr << "to_string(msg1) = " << msg1_tostring << endl;
cerr << "to_string(msg1str) = " << msg1str << endl;
}
util::buffer wr_buf;
binary_serializer bs(&wr_buf, &addressing);
bs << msg1;
binary_deserializer bd(wr_buf.data(), wr_buf.size(), &addressing);
object obj1;
bd >> obj1;
object obj2 = from_string(to_string(msg1));
CPPA_CHECK(obj1 == obj2);
if (typeid(any_tuple) == *(obj1.type()) && obj2.type() == obj1.type()) {
auto& content1 = get<any_tuple>(obj1);
auto& content2 = get<any_tuple>(obj2);
auto opt1 = tuple_cast<decltype(42), string>(content1);
auto opt2 = tuple_cast<decltype(42), string>(content2);
CPPA_CHECK(opt1.valid() && opt2.valid());
if (opt1.valid() && opt2.valid()) {
auto& tup1 = *opt1;
auto& tup2 = *opt2;
CPPA_CHECK_EQUAL(tup1.size(), 2);
CPPA_CHECK_EQUAL(tup2.size(), 2);
CPPA_CHECK_EQUAL(get<0>(tup1), 42);
CPPA_CHECK_EQUAL(get<0>(tup2), 42);
CPPA_CHECK_EQUAL(get<1>(tup1), "Hello \"World\"!");
CPPA_CHECK_EQUAL(get<1>(tup2), "Hello \"World\"!");
}
}
else {
CPPA_FAILURE("obj.type() != typeid(message)");
}
*/
}
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
CPPA_CHECK((is_iterable<int>::value) == false);
// string is primitive and thus not identified by is_iterable
CPPA_CHECK((is_iterable<string>::value) == false);
......@@ -333,71 +293,5 @@ int main() {
}
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
try
{ // test serializers / deserializers with struct_b
// get meta object for struct_b
/*
announce<struct_b>(compound_member(&struct_b::a,
&struct_a::x,
&struct_a::y),
&struct_b::z,
&struct_b::ints);
// testees
struct_b b1 = { { 1, 2 }, 3, list<int>{ 4, 5, 6, 7, 8, 9, 10 } };
struct_b b2;
struct_b b3;
// expected result of to_string(&b1, meta_b)
auto b1str = "struct_b ( struct_a ( 1, 2 ), 3, "
"{ 4, 5, 6, 7, 8, 9, 10 } )";
// verify
CPPA_CHECK_EQUAL((to_string(b1)), b1str); {
// serialize b1 to buf
util::buffer wr_buf;
binary_serializer bs(&wr_buf, &addressing);
bs << b1;
// deserialize b2 from buf
binary_deserializer bd(wr_buf.data(), wr_buf.size(), &addressing);
object res;
bd >> res;
CPPA_CHECK_EQUAL(res.type()->name(), "struct_b");
b2 = get<struct_b>(res);
}
// verify result of serialization / deserialization
CPPA_CHECK(b1 == b2);
CPPA_CHECK_EQUAL(to_string(b2), b1str);
{ // deserialize b3 from string
object res = from_string(b1str);
CPPA_CHECK_EQUAL(res.type()->name(), "struct_b");
b3 = get<struct_b>(res);
}
CPPA_CHECK(b1 == b3);
*/
}
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
try { // test serializers / deserializers with struct_c
// get meta type of struct_c and "announce"
/*
announce<struct_c>(&struct_c::strings, &struct_c::ints);
// testees
struct_c c1{strmap{{"abc", u"cba" }, { "x", u"y" }}, set<int>{9, 4, 5}};
struct_c c2; {
// serialize c1 to buf
util::buffer wr_buf;
binary_serializer bs(&wr_buf, &addressing);
bs << c1;
// serialize c2 from buf
binary_deserializer bd(wr_buf.data(), wr_buf.size(), &addressing);
object res;
bd >> res;
CPPA_CHECK_EQUAL(res.type()->name(), "struct_c");
c2 = get<struct_c>(res);
}
// verify result of serialization / deserialization
CPPA_CHECK(c1 == c2);
*/
}
catch (exception& e) { CPPA_FAILURE(to_verbose_string(e)); }
return CPPA_TEST_RESULT();
}
......@@ -6,16 +6,7 @@
#include "test.hpp"
#include "ping_pong.hpp"
#include "cppa/on.hpp"
#include "cppa/cppa.hpp"
#include "cppa/actor.hpp"
#include "cppa/factory.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/sb_actor.hpp"
#include "cppa/to_string.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/util/type_traits.hpp"
#include "cppa/event_based_actor.hpp"
using namespace std;
using namespace cppa;
......@@ -64,7 +55,7 @@ class event_testee : public sb_actor<event_testee> {
};
// quits after 5 timeouts
actor_ptr spawn_event_testee2() {
actor spawn_event_testee2() {
struct impl : sb_actor<impl> {
behavior wait4timeout(int remaining) {
return (
......@@ -84,7 +75,7 @@ actor_ptr spawn_event_testee2() {
struct chopstick : public sb_actor<chopstick> {
behavior taken_by(actor_ptr whom) {
behavior taken_by(actor whom) {
return (
on<atom("take")>() >> [=] {
return atom("busy");
......@@ -92,7 +83,7 @@ struct chopstick : public sb_actor<chopstick> {
on(atom("put"), whom) >> [=] {
become(available);
},
on(atom("break")) >> [=]() {
on(atom("break")) >> [=] {
quit();
}
);
......@@ -104,7 +95,7 @@ struct chopstick : public sb_actor<chopstick> {
chopstick() {
available = (
on(atom("take"), arg_match) >> [=](actor_ptr whom) -> atom_value {
on(atom("take"), arg_match) >> [=](actor whom) -> atom_value {
become(taken_by(whom));
return atom("taken");
},
......@@ -118,9 +109,9 @@ struct chopstick : public sb_actor<chopstick> {
class testee_actor {
void wait4string() {
void wait4string(blocking_untyped_actor* self) {
bool string_received = false;
do_receive (
self->do_receive (
on<string>() >> [&] {
string_received = true;
},
......@@ -131,9 +122,9 @@ class testee_actor {
.until(gref(string_received));
}
void wait4float() {
void wait4float(blocking_untyped_actor* self) {
bool float_received = false;
do_receive (
self->do_receive (
on<float>() >> [&] {
float_received = true;
},
......@@ -142,15 +133,15 @@ class testee_actor {
}
)
.until(gref(float_received));
wait4string();
wait4string(self);
}
public:
void operator()() {
receive_loop (
void operator()(blocking_untyped_actor* self) {
self->receive_loop (
on<int>() >> [&] {
wait4float();
wait4float(self);
},
on<atom("get_state")>() >> [&] {
return "wait4int";
......@@ -160,40 +151,43 @@ class testee_actor {
};
// receives one timeout and quits
void testee1() {
become(after(chrono::milliseconds(10)) >> [] { unbecome(); });
// self->receives one timeout and quits
void testee1(untyped_actor* self) {
self->become(after(chrono::milliseconds(10)) >> [=] { self->unbecome(); });
}
void testee2(actor_ptr other) {
void testee2(untyped_actor* self, actor other) {
self->link_to(other);
send(other, uint32_t(1));
become (
on<uint32_t>() >> [](uint32_t sleep_time) {
self->send(other, uint32_t(1));
self->become (
on<uint32_t>() >> [=](uint32_t sleep_time) {
// "sleep" for sleep_time milliseconds
become (
self->become (
keep_behavior,
after(chrono::milliseconds(sleep_time)) >> [] { unbecome(); }
after(chrono::milliseconds(sleep_time)) >> [=] {
self->unbecome();
}
);
}
);
}
template<class Testee>
string behavior_test(actor_ptr et) {
string behavior_test(actor et) {
scoped_actor self;
string result;
string testee_name = detail::to_uniform_name(typeid(Testee));
send(et, 1);
send(et, 2);
send(et, 3);
send(et, .1f);
send(et, "hello " + testee_name);
send(et, .2f);
send(et, .3f);
send(et, "hello again " + testee_name);
send(et, "goodbye " + testee_name);
send(et, atom("get_state"));
receive (
self->send(et, 1);
self->send(et, 2);
self->send(et, 3);
self->send(et, .1f);
self->send(et, "hello " + testee_name);
self->send(et, .2f);
self->send(et, .3f);
self->send(et, "hello again " + testee_name);
self->send(et, "goodbye " + testee_name);
self->send(et, atom("get_state"));
self->receive (
on_arg_match >> [&](const string& str) {
result = str;
},
......@@ -202,7 +196,7 @@ string behavior_test(actor_ptr et) {
throw runtime_error(testee_name + " does not reply");
}
);
send_exit(et, exit_reason::user_shutdown);
self->send_exit(et, exit_reason::user_shutdown);
await_all_actors_done();
return result;
}
......@@ -259,9 +253,9 @@ class fixed_stack : public sb_actor<fixed_stack> {
};
void echo_actor() {
become (
others() >> []() -> any_tuple {
behavior echo_actor(untyped_actor* self) {
return (
others() >> [=]() -> any_tuple {
self->quit(exit_reason::normal);
return self->last_dequeued();
}
......@@ -272,26 +266,26 @@ void echo_actor() {
struct simple_mirror : sb_actor<simple_mirror> {
behavior init_state = (
others() >> [] {
return self->last_dequeued();
others() >> [=] {
return last_dequeued();
}
);
};
void high_priority_testee() {
send(self, atom("b"));
send({self, message_priority::high}, atom("a"));
// 'a' must be received before 'b'
become (
on(atom("b")) >> [] {
behavior high_priority_testee(untyped_actor* self) {
self->send(self, atom("b"));
self->send(message_priority::high, self, atom("a"));
// 'a' must be self->received before 'b'
return (
on(atom("b")) >> [=] {
CPPA_FAILURE("received 'b' before 'a'");
self->quit();
},
on(atom("a")) >> [] {
on(atom("a")) >> [=] {
CPPA_CHECKPOINT();
become (
on(atom("b")) >> [] {
self->become (
on(atom("b")) >> [=] {
CPPA_CHECKPOINT();
self->quit();
},
......@@ -302,65 +296,65 @@ void high_priority_testee() {
);
}
struct high_priority_testee_class : event_based_actor {
void init() {
high_priority_testee();
struct high_priority_testee_class : untyped_actor {
behavior make_behavior() override {
return high_priority_testee(this);
}
};
struct master : event_based_actor {
void init() override {
become(
on(atom("done")) >> []() {
self->quit(exit_reason::user_shutdown);
struct master : untyped_actor {
behavior make_behavior() override {
return (
on(atom("done")) >> [=] {
quit(exit_reason::user_shutdown);
}
);
}
};
struct slave : event_based_actor {
struct slave : untyped_actor {
slave(actor_ptr master) : master{master} { }
slave(actor master) : master{master} { }
void init() override {
behavior make_behavior() override {
link_to(master);
become (
return (
others() >> CPPA_UNEXPECTED_MSG_CB()
);
}
actor_ptr master;
actor master;
};
void test_serial_reply() {
auto mirror_behavior = [](int num) {
become(others() >> [=]() -> any_tuple {
auto mirror_behavior = [=](untyped_actor* self, int num) {
self->become(others() >> [=]() -> any_tuple {
cout << "right back at you from " << num
<< "; ID: " << self->id() << endl;
return self->last_dequeued();
});
};
auto master = spawn([=] {
auto master = spawn([=](untyped_actor* self) {
cout << "ID of master: " << self->id() << endl;
// produce 5 mirror actors
auto c0 = spawn<linked>(mirror_behavior, 0);
auto c1 = spawn<linked>(mirror_behavior, 1);
auto c2 = spawn<linked>(mirror_behavior, 2);
auto c3 = spawn<linked>(mirror_behavior, 3);
auto c4 = spawn<linked>(mirror_behavior, 4);
become (
auto c0 = self->spawn<linked>(mirror_behavior, 0);
auto c1 = self->spawn<linked>(mirror_behavior, 1);
auto c2 = self->spawn<linked>(mirror_behavior, 2);
auto c3 = self->spawn<linked>(mirror_behavior, 3);
auto c4 = self->spawn<linked>(mirror_behavior, 4);
self->become (
on(atom("hi there")) >> [=] {
// *
return sync_send(c0, atom("sub0")).then(
return self->sync_send(c0, atom("sub0")).then(
on(atom("sub0")) >> [=] {
return sync_send(c1, atom("sub1")).then(
return self->sync_send(c1, atom("sub1")).then(
on(atom("sub1")) >> [=] {
return sync_send(c2, atom("sub2")).then(
return self->sync_send(c2, atom("sub2")).then(
on(atom("sub2")) >> [=] {
return sync_send(c3, atom("sub3")).then(
return self->sync_send(c3, atom("sub3")).then(
on(atom("sub3")) >> [=] {
return sync_send(c4, atom("sub4")).then(
return self->sync_send(c4, atom("sub4")).then(
on(atom("sub4")) >> [=] {
return atom("hiho");
}
......@@ -373,24 +367,25 @@ void test_serial_reply() {
);
}
);
//*/
//return atom("hiho");
}
);
});
cout << "ID of main: " << self->id() << endl;
sync_send(master, atom("hi there")).await(
on(atom("hiho")) >> [] {
CPPA_CHECKPOINT();
},
others() >> CPPA_UNEXPECTED_MSG_CB()
);
send_exit(master, exit_reason::user_shutdown);
{ // lifetime scope of self
scoped_actor self;
cout << "ID of main: " << self->id() << endl;
self->sync_send(master, atom("hi there")).await(
on(atom("hiho")) >> [] {
CPPA_CHECKPOINT();
},
others() >> CPPA_UNEXPECTED_MSG_CB()
);
self->send_exit(master, exit_reason::user_shutdown);
}
await_all_actors_done();
}
void test_or_else() {
scoped_actor self;
partial_function handle_a {
on("a") >> [] { return 1; }
};
......@@ -400,32 +395,28 @@ void test_or_else() {
partial_function handle_c {
on("c") >> [] { return 3; }
};
auto run_testee([](actor_ptr testee) {
sync_send(testee, "a").await([](int i) {
auto run_testee([&](actor testee) {
self->sync_send(testee, "a").await([](int i) {
CPPA_CHECK_EQUAL(i, 1);
});
sync_send(testee, "b").await([](int i) {
self->sync_send(testee, "b").await([](int i) {
CPPA_CHECK_EQUAL(i, 2);
});
sync_send(testee, "c").await([](int i) {
self->sync_send(testee, "c").await([](int i) {
CPPA_CHECK_EQUAL(i, 3);
});
send_exit(testee, exit_reason::user_shutdown);
await_all_actors_done();
self->send_exit(testee, exit_reason::user_shutdown);
self->await_all_other_actors_done();
});
run_testee(
spawn([=] {
become(handle_a.or_else(handle_b).or_else(handle_c));
return handle_a.or_else(handle_b).or_else(handle_c);
})
);
run_testee(
spawn([=] {
become(handle_a, handle_b, handle_c);
})
);
run_testee(
spawn([=] {
become(
return (
handle_a.or_else(handle_b),
on("c") >> [] { return 3; }
);
......@@ -433,7 +424,7 @@ void test_or_else() {
);
run_testee(
spawn([=] {
become(
return (
on("a") >> [] { return 1; },
handle_b.or_else(handle_c)
);
......@@ -443,8 +434,8 @@ void test_or_else() {
void test_continuation() {
auto mirror = spawn<simple_mirror>();
spawn([=] {
sync_send(mirror, 42).then(
spawn([=](untyped_actor* self) {
self->sync_send(mirror, 42).then(
on(42) >> [] {
return "fourty-two";
}
......@@ -456,7 +447,7 @@ void test_continuation() {
).continue_with(
[=](float f) {
CPPA_CHECK_EQUAL(f, 4.2f);
send_exit(mirror, exit_reason::user_shutdown);
self->send_exit(mirror, exit_reason::user_shutdown);
self->quit();
}
);
......@@ -464,8 +455,8 @@ void test_continuation() {
await_all_actors_done();
}
int main() {
CPPA_TEST(test_spawn);
void test_spawn() {
scoped_actor self;
test_serial_reply();
test_or_else();
......@@ -475,36 +466,36 @@ int main() {
auto m = spawn<master, detached>();
spawn<slave>(m);
spawn<slave>(m);
send(m, atom("done"));
self->send(m, atom("done"));
await_all_actors_done();
CPPA_CHECKPOINT();
CPPA_PRINT("test send()");
send(self, 1, 2, 3, true);
receive(on(1, 2, 3, true) >> [] { });
self << any_tuple{};
receive(on() >> [] { });
CPPA_PRINT("test self->send()");
self->send(self, 1, 2, 3, true);
self->receive(on(1, 2, 3, true) >> [] { });
self->send(self, any_tuple{});
self->receive(on() >> [] { });
CPPA_CHECKPOINT();
self << any_tuple{};
receive(on() >> [] { });
self->send(self, any_tuple{});
self->receive(on() >> [] { });
CPPA_PRINT("test receive with zero timeout");
receive (
CPPA_PRINT("test self->receive with zero timeout");
self->receive (
others() >> CPPA_UNEXPECTED_MSG_CB(),
after(chrono::seconds(0)) >> [] { /* mailbox empty */ }
);
CPPA_CHECKPOINT();
CPPA_PRINT("test mirror"); {
auto mirror = spawn<simple_mirror, monitored>();
send(mirror, "hello mirror");
receive (
auto mirror = self->spawn<simple_mirror, monitored>();
self->send(mirror, "hello mirror");
self->receive (
on("hello mirror") >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
send_exit(mirror, exit_reason::user_shutdown);
receive (
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
......@@ -513,14 +504,14 @@ int main() {
}
CPPA_PRINT("test detached mirror"); {
auto mirror = spawn<simple_mirror, monitored+detached>();
send(mirror, "hello mirror");
receive (
auto mirror = self->spawn<simple_mirror, monitored+detached>();
self->send(mirror, "hello mirror");
self->receive (
on("hello mirror") >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
send_exit(mirror, exit_reason::user_shutdown);
receive (
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
......@@ -529,15 +520,15 @@ int main() {
}
CPPA_PRINT("test priority aware mirror"); {
auto mirror = spawn<simple_mirror, monitored+priority_aware>();
auto mirror = self->spawn<simple_mirror, monitored+priority_aware>();
CPPA_CHECKPOINT();
send(mirror, "hello mirror");
receive (
self->send(mirror, "hello mirror");
self->receive (
on("hello mirror") >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
send_exit(mirror, exit_reason::user_shutdown);
receive (
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
......@@ -547,8 +538,8 @@ int main() {
CPPA_PRINT("test echo actor");
auto mecho = spawn(echo_actor);
send(mecho, "hello echo");
receive (
self->send(mecho, "hello echo");
self->receive (
on("hello echo") >> [] { },
others() >> CPPA_UNEXPECTED_MSG_CB()
);
......@@ -556,12 +547,12 @@ int main() {
CPPA_CHECKPOINT();
CPPA_PRINT("test delayed_send()");
delayed_send(self, chrono::seconds(1), 1, 2, 3);
receive(on(1, 2, 3) >> [] { });
self->delayed_send(self, chrono::seconds(1), 1, 2, 3);
self->receive(on(1, 2, 3) >> [] { });
CPPA_CHECKPOINT();
CPPA_PRINT("test timeout");
receive(after(chrono::seconds(1)) >> [] { });
self->receive(after(chrono::seconds(1)) >> [] { });
CPPA_CHECKPOINT();
spawn(testee1);
......@@ -573,42 +564,11 @@ int main() {
CPPA_CHECKPOINT();
auto cstk = spawn<chopstick>();
send(cstk, atom("take"), self);
receive (
self->send(cstk, atom("take"), self);
self->receive (
on(atom("taken")) >> [&]() {
send(cstk, atom("put"), self);
send(cstk, atom("break"));
}
);
await_all_actors_done();
CPPA_CHECKPOINT();
auto factory = factory::event_based([&](int* i, float*, string*) {
become (
on(atom("get_int")) >> [i] {
return *i;
},
on(atom("set_int"), arg_match) >> [i](int new_value) {
*i = new_value;
},
on(atom("done")) >> [] {
self->quit();
}
);
});
auto foobaz_actor = factory.spawn(23);
send(foobaz_actor, atom("get_int"));
send(foobaz_actor, atom("set_int"), 42);
send(foobaz_actor, atom("get_int"));
send(foobaz_actor, atom("done"));
receive (
on_arg_match >> [&](int value) {
CPPA_CHECK_EQUAL(value, 23);
}
);
receive (
on_arg_match >> [&](int value) {
CPPA_CHECK_EQUAL(value, 42);
self->send(cstk, atom("put"), self);
self->send(cstk, atom("break"));
}
);
await_all_actors_done();
......@@ -616,13 +576,13 @@ int main() {
auto st = spawn<fixed_stack>(10);
// push 20 values
for (int i = 0; i < 20; ++i) send(st, atom("push"), i);
for (int i = 0; i < 20; ++i) self->send(st, atom("push"), i);
// pop 20 times
for (int i = 0; i < 20; ++i) send(st, atom("pop"));
for (int i = 0; i < 20; ++i) self->send(st, atom("pop"));
// expect 10 failure messages
{
int i = 0;
receive_for(i, 10) (
self->receive_for(i, 10) (
on(atom("failure")) >> [] { }
);
CPPA_CHECKPOINT();
......@@ -631,7 +591,7 @@ int main() {
{
vector<int> values;
int i = 0;
receive_for(i, 10) (
self->receive_for(i, 10) (
on(atom("ok"), arg_match) >> [&](int value) {
values.push_back(value);
}
......@@ -640,31 +600,31 @@ int main() {
CPPA_CHECK_EQUAL(util::join(values, ","), util::join(values, ","));
}
// terminate st
send_exit(st, exit_reason::user_shutdown);
self->send_exit(st, exit_reason::user_shutdown);
await_all_actors_done();
CPPA_CHECKPOINT();
auto sync_testee1 = spawn<blocking_api>([] {
receive (
auto sync_testee1 = spawn<blocking_api>([](blocking_untyped_actor* self) {
self->receive (
on(atom("get")) >> [] {
return make_cow_tuple(42, 2);
}
);
});
send(self, 0, 0);
auto handle = sync_send(sync_testee1, atom("get"));
self->send(self, 0, 0);
auto handle = self->sync_send(sync_testee1, atom("get"));
// wait for some time (until sync response arrived in mailbox)
receive (after(chrono::milliseconds(50)) >> [] { });
// enqueue async messages (must be skipped by receive_response)
send(self, 42, 1);
self->receive (after(chrono::milliseconds(50)) >> [] { });
// enqueue async messages (must be skipped by self->receive_response)
self->send(self, 42, 1);
// must skip sync message
receive (
self->receive (
on(42, arg_match) >> [&](int i) {
CPPA_CHECK_EQUAL(i, 1);
}
);
// must skip remaining async message
receive_response (handle) (
self->receive_response (handle) (
on_arg_match >> [&](int a, int b) {
CPPA_CHECK_EQUAL(a, 42);
CPPA_CHECK_EQUAL(b, 2);
......@@ -673,56 +633,52 @@ int main() {
after(chrono::seconds(10)) >> CPPA_UNEXPECTED_TOUT_CB()
);
// dequeue remaining async. message
receive (on(0, 0) >> CPPA_CHECKPOINT_CB());
self->receive (on(0, 0) >> CPPA_CHECKPOINT_CB());
// make sure there's no other message in our mailbox
receive (
self->receive (
others() >> CPPA_UNEXPECTED_MSG_CB(),
after(chrono::seconds(0)) >> [] { }
);
await_all_actors_done();
CPPA_CHECKPOINT();
CPPA_PRINT("test sync send with factory spawned actor");
auto sync_testee_factory = factory::event_based(
[&]() {
become (
on("hi") >> [&]() {
auto handle = sync_send(self->last_sender(), "whassup?");
handle_response(handle) (
on_arg_match >> [&](const string& str) {
CPPA_CHECK(self->last_sender() != nullptr);
CPPA_CHECK_EQUAL(str, "nothing");
self->quit();
// TODO: should return the value instead
send(self->last_sender(), "goodbye!");
},
after(chrono::minutes(1)) >> [] {
cerr << "PANIC!!!!" << endl;
abort();
}
);
},
others() >> CPPA_UNEXPECTED_MSG_CB()
);
}
);
CPPA_PRINT("test sync send");
CPPA_CHECKPOINT();
auto sync_testee = sync_testee_factory.spawn();
auto sync_testee = spawn<blocking_api>([](blocking_untyped_actor* self) {
self->receive (
on("hi", arg_match) >> [&](actor from) {
self->sync_send(from, "whassup?", self).await(
on_arg_match >> [&](const string& str) -> string {
CPPA_CHECK(self->last_sender() != nullptr);
CPPA_CHECK_EQUAL(str, "nothing");
self->quit();
return "goodbye!";
},
after(chrono::minutes(1)) >> [] {
cerr << "PANIC!!!!" << endl;
abort();
}
);
},
others() >> CPPA_UNEXPECTED_MSG_CB()
);
});
self->monitor(sync_testee);
send(sync_testee, "hi");
receive (
on("whassup?") >> [&]() -> std::string {
self->send(sync_testee, "hi", self);
self->receive (
on("whassup?", arg_match) >> [&](actor other) -> std::string {
CPPA_CHECKPOINT();
// this is NOT a reply, it's just an asynchronous message
send(self->last_sender(), "a lot!");
self->send(other, "a lot!");
return "nothing";
}
);
receive (
self->receive (
on("goodbye!") >> CPPA_CHECKPOINT_CB(),
after(std::chrono::seconds(5)) >> CPPA_UNEXPECTED_TOUT_CB()
);
receive (
self->receive (
on(atom("DOWN"), exit_reason::normal) >> [&] {
CPPA_CHECK_EQUAL(self->last_sender(), sync_testee);
}
......@@ -730,125 +686,83 @@ int main() {
await_all_actors_done();
CPPA_CHECKPOINT();
sync_send(sync_testee, "!?").await(
self->sync_send(sync_testee, "!?").await(
on(atom("EXITED"), any_vals) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB(),
after(chrono::milliseconds(5)) >> CPPA_UNEXPECTED_TOUT_CB()
);
auto inflater = factory::event_based(
[](string*, actor_ptr* receiver) {
become(
on_arg_match >> [=](int n, const string& s) {
send(*receiver, n * 2, s);
},
on(atom("done")) >> [] {
self->quit();
}
);
}
);
auto joe = inflater.spawn("Joe", self);
auto bob = inflater.spawn("Bob", joe);
send(bob, 1, "hello actor");
receive (
auto inflater = [](untyped_actor* self, const string&, actor buddy) {
self->become(
on_arg_match >> [=](int n, const string& s) {
self->send(buddy, n * 2, s);
},
on(atom("done")) >> [=] {
self->quit();
}
);
};
auto joe = spawn(inflater, "Joe", self);
auto bob = spawn(inflater, "Bob", joe);
self->send(bob, 1, "hello actor");
self->receive (
on(4, "hello actor") >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
// kill joe and bob
auto poison_pill = make_any_tuple(atom("done"));
joe << poison_pill;
bob << poison_pill;
anon_send(joe, poison_pill);
anon_send(bob, poison_pill);
await_all_actors_done();
function<actor_ptr (const string&, const actor_ptr&)> spawn_next;
auto kr34t0r = factory::event_based(
// it's safe to pass spawn_next as reference here, because
// - it is guaranteeed to outlive kr34t0r by general scoping rules
// - the lambda is always executed in the current actor's thread
// but using spawn_next in a message handler could
// still cause undefined behavior!
[&spawn_next](string* name, actor_ptr* pal) {
if (*name == "Joe" && !*pal) {
*pal = spawn_next("Bob", self);
}
become (
others() >> [pal]() {
// forward message and die
*pal << self->last_dequeued();
self->quit();
}
);
function<actor (const string&, const actor&)> spawn_next;
// it's safe to capture spawn_next as reference here, because
// - it is guaranteeed to outlive kr34t0r by general scoping rules
// - the lambda is always executed in the current actor's thread
// but using spawn_next in a message handler could
// still cause undefined behavior!
auto kr34t0r = [&spawn_next](untyped_actor* self, const string& name, actor pal) {
if (name == "Joe" && !pal) {
pal = spawn_next("Bob", self);
}
);
spawn_next = [&kr34t0r](const string& name, const actor_ptr& pal) {
return kr34t0r.spawn(name, pal);
self->become (
others() >> [=] {
// forward message and die
self->send_tuple(pal, self->last_dequeued());
self->quit();
}
);
};
auto joe_the_second = kr34t0r.spawn("Joe");
send(joe_the_second, atom("done"));
spawn_next = [&kr34t0r](const string& name, const actor& pal) {
return spawn(kr34t0r, name, pal);
};
auto joe_the_second = spawn(kr34t0r, "Joe", invalid_actor);
self->send(joe_the_second, atom("done"));
await_all_actors_done();
int zombie_init_called = 0;
int zombie_on_exit_called = 0;
factory::event_based([&]() { ++zombie_init_called; },
[&]() { ++zombie_on_exit_called; }).spawn();
CPPA_CHECK_EQUAL(zombie_init_called, 1);
CPPA_CHECK_EQUAL(zombie_on_exit_called, 1);
factory::event_based([&](int* i) {
CPPA_CHECK_EQUAL(*i, 42);
++zombie_init_called;
},
[&](int* i) {
CPPA_CHECK_EQUAL(*i, 42);
++zombie_on_exit_called;
})
.spawn(42);
CPPA_CHECK_EQUAL(zombie_init_called, 2);
CPPA_CHECK_EQUAL(zombie_on_exit_called, 2);
factory::event_based([&](int* i) {
CPPA_CHECK_EQUAL(*i, 23);
++zombie_init_called;
},
[&]() {
++zombie_on_exit_called;
})
.spawn(23);
CPPA_CHECK_EQUAL(zombie_init_called, 3);
CPPA_CHECK_EQUAL(zombie_on_exit_called, 3);
auto f = factory::event_based([](string* name) {
become (
auto f = [](const string& name) -> behavior {
return (
on(atom("get_name")) >> [name] {
return make_cow_tuple(atom("name"), *name);
return make_cow_tuple(atom("name"), name);
}
);
});
auto a1 = f.spawn("alice");
auto a2 = f.spawn("bob");
send(a1, atom("get_name"));
receive (
};
auto a1 = spawn(f, "alice");
auto a2 = spawn(f, "bob");
self->send(a1, atom("get_name"));
self->receive (
on(atom("name"), arg_match) >> [&](const string& name) {
CPPA_CHECK_EQUAL(name, "alice");
}
);
send(a2, atom("get_name"));
receive (
self->send(a2, atom("get_name"));
self->receive (
on(atom("name"), arg_match) >> [&](const string& name) {
CPPA_CHECK_EQUAL(name, "bob");
}
);
send_exit(a1, exit_reason::user_shutdown);
send_exit(a2, exit_reason::user_shutdown);
await_all_actors_done();
factory::event_based([](int* i) {
become(
after(chrono::milliseconds(50)) >> [=]() {
if (++(*i) >= 5) self->quit();
}
);
}).spawn();
self->send_exit(a1, exit_reason::user_shutdown);
self->send_exit(a2, exit_reason::user_shutdown);
await_all_actors_done();
auto res1 = behavior_test<testee_actor>(spawn<blocking_api>(testee_actor{}));
......@@ -857,25 +771,25 @@ int main() {
// create some actors linked to one single actor
// and kill them all through killing the link
auto legion = spawn([] {
auto legion = spawn([](untyped_actor* self) {
CPPA_LOGF_INFO("spawn 1, 000 actors");
for (int i = 0; i < 1000; ++i) {
spawn<event_testee, linked>();
self->spawn<event_testee, linked>();
}
become(others() >> CPPA_UNEXPECTED_MSG_CB());
self->become(others() >> CPPA_UNEXPECTED_MSG_CB());
});
send_exit(legion, exit_reason::user_shutdown);
self->send_exit(legion, exit_reason::user_shutdown);
await_all_actors_done();
CPPA_CHECKPOINT();
self->trap_exit(true);
auto ping_actor = spawn<monitored+blocking_api>(ping, 10);
auto pong_actor = spawn<monitored+blocking_api>(pong, ping_actor);
auto ping_actor = self->spawn<monitored+blocking_api>(ping, 10);
auto pong_actor = self->spawn<monitored+blocking_api>(pong, ping_actor);
self->link_to(pong_actor);
int i = 0;
int flags = 0;
delayed_send(self, chrono::seconds(1), atom("FooBar"));
self->delayed_send(self, chrono::seconds(1), atom("FooBar"));
// wait for DOWN and EXIT messages of pong
receive_for(i, 4) (
self->receive_for(i, 4) (
on(atom("EXIT"), arg_match) >> [&](uint32_t reason) {
CPPA_CHECK_EQUAL(exit_reason::user_shutdown, reason);
CPPA_CHECK(self->last_sender() == pong_actor);
......@@ -915,20 +829,17 @@ int main() {
spawn<high_priority_testee_class, priority_aware>();
await_all_actors_done();
// don't try this at home, kids
send(self, atom("check"));
try {
become (
on(atom("check")) >> [] {
CPPA_CHECKPOINT();
self->quit();
}
);
self->exec_behavior_stack();
CPPA_FAILURE("line " << __LINE__ << " should be unreachable");
}
catch (actor_exited&) {
CPPA_CHECKPOINT();
}
self->send(self, atom("check"));
self->receive (
on(atom("check")) >> [] {
CPPA_CHECKPOINT();
}
);
}
int main() {
CPPA_TEST(test_spawn);
test_spawn();
shutdown();
return CPPA_TEST_RESULT();
}
......@@ -7,7 +7,7 @@ using namespace cppa::placeholders;
struct sync_mirror : sb_actor<sync_mirror> {
behavior init_state = (
others() >> [] { return self->last_dequeued(); }
others() >> [=] { return last_dequeued(); }
);
};
......@@ -19,13 +19,13 @@ struct float_or_int : sb_actor<float_or_int> {
);
};
struct popular_actor : event_based_actor { // popular actors have a buddy
actor_ptr m_buddy;
popular_actor(const actor_ptr& buddy) : m_buddy(buddy) { }
inline const actor_ptr& buddy() const { return m_buddy; }
struct popular_actor : untyped_actor { // popular actors have a buddy
actor m_buddy;
popular_actor(const actor& buddy) : m_buddy(buddy) { }
inline const actor& buddy() const { return m_buddy; }
void report_failure() {
send(buddy(), atom("failure"));
self->quit();
quit();
}
};
......@@ -45,10 +45,10 @@ struct popular_actor : event_based_actor { // popular actors have a buddy
\******************************************************************************/
struct A : popular_actor {
A(const actor_ptr& buddy) : popular_actor(buddy) { }
void init() {
become (
on(atom("go"), arg_match) >> [=](const actor_ptr& next) {
A(const actor& buddy) : popular_actor(buddy) { }
behavior make_behavior() override {
return (
on(atom("go"), arg_match) >> [=](const actor& next) {
CPPA_CHECKPOINT();
sync_send(next, atom("gogo")).then([=] {
CPPA_CHECKPOINT();
......@@ -62,9 +62,9 @@ struct A : popular_actor {
};
struct B : popular_actor {
B(const actor_ptr& buddy) : popular_actor(buddy) { }
void init() {
become (
B(const actor& buddy) : popular_actor(buddy) { }
behavior make_behavior() override {
return (
others() >> [=] {
CPPA_CHECKPOINT();
forward_to(buddy());
......@@ -77,7 +77,7 @@ struct B : popular_actor {
struct C : sb_actor<C> {
behavior init_state = (
on(atom("gogo")) >> [=]() -> atom_value {
self->quit();
quit();
return atom("gogogo");
}
);
......@@ -100,8 +100,8 @@ struct C : sb_actor<C> {
\******************************************************************************/
struct D : popular_actor {
D(const actor_ptr& buddy) : popular_actor(buddy) { }
void init() {
D(const actor& buddy) : popular_actor(buddy) { }
behavior make_behavior() override {
become (
others() >> [=] {
/*
......@@ -136,13 +136,12 @@ struct D : popular_actor {
* X *
\******************************************************************************/
struct server : event_based_actor {
struct server : untyped_actor {
void init() {
behavior make_behavior() override {
auto die = [=] { quit(exit_reason::user_shutdown); };
become (
on(atom("idle")) >> [=] {
auto worker = last_sender();
on(atom("idle"), arg_match) >> [=](actor worker) {
become (
keep_behavior,
on(atom("request")) >> [=] {
......@@ -160,33 +159,29 @@ struct server : event_based_actor {
};
int main() {
CPPA_TEST(test_sync_send);
self->on_sync_failure([] {
void test_sync_send() {
scoped_actor self;
self->on_sync_failure([&] {
CPPA_FAILURE("received: " << to_string(self->last_dequeued()));
});
spawn<monitored + blocking_api>([] {
self->spawn<monitored + blocking_api>([](blocking_untyped_actor* self) {
CPPA_LOGC_TRACE("NONE", "main$sync_failure_test", "id = " << self->id());
int invocations = 0;
auto foi = spawn<float_or_int, linked>();
send(foi, atom("i"));
receive(on_arg_match >> [](int i) { CPPA_CHECK_EQUAL(i, 0); });
self->on_sync_failure([] {
auto foi = self->spawn<float_or_int, linked>();
self->send(foi, atom("i"));
self->receive(on_arg_match >> [](int i) { CPPA_CHECK_EQUAL(i, 0); });
self->on_sync_failure([=] {
CPPA_FAILURE("received: " << to_string(self->last_dequeued()));
});
sync_send(foi, atom("i")).then(
self->sync_send(foi, atom("i")).await(
[&](int i) { CPPA_CHECK_EQUAL(i, 0); ++invocations; },
[&](float) { CPPA_UNEXPECTED_MSG(); }
)
.continue_with([&] {
sync_send(foi, atom("f")).then(
[&](int) { CPPA_UNEXPECTED_MSG(); },
[&](float f) { CPPA_CHECK_EQUAL(f, 0); ++invocations; }
);
});
self->exec_behavior_stack();
);
self->sync_send(foi, atom("f")).await(
[&](int) { CPPA_UNEXPECTED_MSG(); },
[&](float f) { CPPA_CHECK_EQUAL(f, 0); ++invocations; }
);
//self->exec_behavior_stack();
CPPA_CHECK_EQUAL(invocations, 2);
CPPA_PRINT("trigger sync failure");
// provoke invocation of self->handle_sync_failure()
......@@ -195,7 +190,7 @@ int main() {
self->on_sync_failure([&] {
sync_failure_called = true;
});
sync_send(foi, atom("f")).await(
self->sync_send(foi, atom("f")).await(
on<int>() >> [&] {
int_handler_called = true;
}
......@@ -204,22 +199,20 @@ int main() {
CPPA_CHECK_EQUAL(int_handler_called, false);
self->quit(exit_reason::user_shutdown);
});
receive (
self->receive (
on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
auto mirror = spawn<sync_mirror>();
bool continuation_called = false;
sync_send(mirror, 42)
.then([](int value) { CPPA_CHECK_EQUAL(value, 42); })
.continue_with([&] { continuation_called = true; });
self->exec_behavior_stack();
self->sync_send(mirror, 42)
.await([](int value) { CPPA_CHECK_EQUAL(value, 42); });
CPPA_CHECK_EQUAL(continuation_called, true);
send_exit(mirror, exit_reason::user_shutdown);
self->send_exit(mirror, exit_reason::user_shutdown);
await_all_actors_done();
CPPA_CHECKPOINT();
auto await_success_message = [&] {
receive (
self->receive (
on(atom("success")) >> CPPA_CHECKPOINT_CB(),
on(atom("failure")) >> CPPA_FAILURE_CB("A didn't receive sync response"),
on(atom("DOWN"), arg_match).when(_x2 != exit_reason::normal)
......@@ -228,23 +221,23 @@ int main() {
}
);
};
send(spawn<A, monitored>(self), atom("go"), spawn<B>(spawn<C>()));
self->send(self->spawn<A, monitored>(self), atom("go"), spawn<B>(spawn<C>()));
await_success_message();
CPPA_CHECKPOINT();
await_all_actors_done();
send(spawn<A, monitored>(self), atom("go"), spawn<D>(spawn<C>()));
self->send(self->spawn<A, monitored>(self), atom("go"), spawn<D>(spawn<C>()));
await_success_message();
CPPA_CHECKPOINT();
await_all_actors_done();
CPPA_CHECKPOINT();
timed_sync_send(self, std::chrono::milliseconds(50), atom("NoWay")).await(
self->timed_sync_send(self, std::chrono::milliseconds(50), atom("NoWay")).await(
on(atom("TIMEOUT")) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
// we should have received two DOWN messages with normal exit reason
// plus 'NoWay'
int i = 0;
receive_for(i, 3) (
self->receive_for(i, 3) (
on(atom("DOWN"), exit_reason::normal) >> CPPA_CHECKPOINT_CB(),
on(atom("NoWay")) >> [] {
CPPA_CHECKPOINT();
......@@ -256,7 +249,7 @@ int main() {
);
CPPA_CHECKPOINT();
// mailbox should be empty now
receive (
self->receive (
others() >> CPPA_UNEXPECTED_MSG_CB(),
after(std::chrono::seconds(0)) >> CPPA_CHECKPOINT_CB()
);
......@@ -266,28 +259,24 @@ int main() {
bool timeout_occured = false;
self->on_sync_timeout([&] { timeout_occured = true; });
self->on_sync_failure(CPPA_UNEXPECTED_MSG_CB());
timed_sync_send(c, std::chrono::milliseconds(500), atom("HiThere"))
.then(CPPA_FAILURE_CB("C replied to 'HiThere'!"))
.continue_with(CPPA_FAILURE_CB("continuation erroneously invoked"));
self->exec_behavior_stack();
self->timed_sync_send(c, std::chrono::milliseconds(500), atom("HiThere"))
.await(CPPA_FAILURE_CB("C replied to 'HiThere'!"));
CPPA_CHECK_EQUAL(timeout_occured, true);
self->on_sync_failure(CPPA_UNEXPECTED_MSG_CB());
sync_send(c, atom("gogo")).then(CPPA_CHECKPOINT_CB())
.continue_with(CPPA_CHECKPOINT_CB());
self->exec_behavior_stack();
send_exit(c, exit_reason::user_shutdown);
self->sync_send(c, atom("gogo")).await(CPPA_CHECKPOINT_CB());
self->send_exit(c, exit_reason::user_shutdown);
await_all_actors_done();
CPPA_CHECKPOINT();
// test use case 3
spawn<monitored + blocking_api>([] { // client
auto s = spawn<server, linked>(); // server
auto w = spawn<linked>([] { // worker
become(on(atom("request")) >> []{ return atom("response"); });
self->spawn<monitored + blocking_api>([](blocking_untyped_actor* self) { // client
auto s = self->spawn<server, linked>(); // server
auto w = self->spawn<linked>([](untyped_actor* self) { // worker
self->become(on(atom("request")) >> []{ return atom("response"); });
});
// first 'idle', then 'request'
send_as(w, s, atom("idle"));
sync_send(s, atom("request")).await(
self->sync_send(s, atom("request")).await(
on(atom("response")) >> [=] {
CPPA_CHECKPOINT();
CPPA_CHECK_EQUAL(self->last_sender(), w);
......@@ -295,23 +284,28 @@ int main() {
others() >> CPPA_UNEXPECTED_MSG_CB()
);
// first 'request', then 'idle'
auto handle = sync_send(s, atom("request"));
auto handle = self->sync_send(s, atom("request"));
send_as(w, s, atom("idle"));
receive_response(handle) (
self->receive_response(handle) (
on(atom("response")) >> [=] {
CPPA_CHECKPOINT();
CPPA_CHECK_EQUAL(self->last_sender(), w);
},
others() >> CPPA_UNEXPECTED_MSG_CB()
);
send(s, "Ever danced with the devil in the pale moonlight?");
self->send(s, "Ever danced with the devil in the pale moonlight?");
// response: {'EXIT', exit_reason::user_shutdown}
receive_loop(others() >> CPPA_UNEXPECTED_MSG_CB());
self->receive_loop(others() >> CPPA_UNEXPECTED_MSG_CB());
});
receive (
self->receive (
on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
}
int main() {
CPPA_TEST(test_sync_send);
test_sync_send();
await_all_actors_done();
CPPA_CHECKPOINT();
shutdown();
......
......@@ -87,9 +87,9 @@ optional<int> str2int(const std::string& str) {
CPPA_FAILURE(#FunName " erroneously invoked"); \
} else { CPPA_CHECKPOINT(); } static_cast<void>(42)
struct dummy_receiver : event_based_actor {
void init() {
become(
struct dummy_receiver : untyped_actor {
behavior make_behavior() override {
return (
on_arg_match >> [=](expensive_copy_struct& ecs) -> expensive_copy_struct {
ecs.value = 42;
quit();
......@@ -425,8 +425,9 @@ void check_move_ops() {
check_move_optional();
CPPA_PRINT(__func__);
CPPA_CHECK_EQUAL(s_expensive_copies.load(), 0);
send(spawn<dummy_receiver>(), expensive_copy_struct());
receive (
scoped_actor self;
self->send(spawn<dummy_receiver>(), expensive_copy_struct());
self->receive (
on_arg_match >> [&](expensive_copy_struct& ecs) {
CPPA_CHECK_EQUAL(42, ecs.value);
}
......
......@@ -41,6 +41,7 @@ bool operator==(const my_request& lhs, const my_request& rhs) {
return lhs.a == rhs.a && lhs.b == rhs.b;
}
/*
typed_actor_ptr<replies_to<my_request>::with<bool>>
spawn_typed_server() {
return spawn_typed(
......@@ -63,10 +64,11 @@ class typed_testee : public typed_actor<replies_to<my_request>::with<bool>> {
}
};
*/
int main() {
CPPA_TEST(test_typed_spawn);
/*
announce<my_request>(&my_request::a, &my_request::b);
auto sptr = spawn_typed_server();
sync_send(sptr, my_request{2, 2}).await(
......@@ -176,5 +178,6 @@ int main() {
await_all_actors_done();
CPPA_CHECKPOINT();
shutdown();
*/
return CPPA_TEST_RESULT();
}
......@@ -134,7 +134,7 @@ int main() {
std::string, std::u16string, std::u32string,
float, double,
atom_value, any_tuple, message_header,
actor_ptr, group_ptr,
actor, group_ptr,
channel, node_id_ptr
>::arr;
......@@ -157,7 +157,7 @@ int main() {
uniform_typeid<atom_value>(),
uniform_typeid<any_tuple>(),
uniform_typeid<message_header>(),
uniform_typeid<actor_ptr>(),
uniform_typeid<actor>(),
uniform_typeid<group_ptr>(),
uniform_typeid<channel>(),
uniform_typeid<node_id_ptr>()
......
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