Commit ef0b744f authored by Dominik Charousset's avatar Dominik Charousset

restructured actor class hierarchy

this patch removes several classes and mixins in the actor class hierarchy and
introduces a new mixin facility (`extend<...>::with<...>`);
this patch also refactores several function signatures to take `actor_ptr`
arguments rather than `actor*`
parent c3beb4d6
......@@ -85,11 +85,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
endif ("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(LIBCPPA_SRC
src/abstract_scheduled_actor.cpp
src/abstract_tuple.cpp
src/actor.cpp
src/actor_addressing.cpp
src/actor_count.cpp
src/actor_proxy.cpp
src/actor_registry.cpp
src/any_tuple.cpp
......
......@@ -18,10 +18,7 @@ cppa/cow_tuple.hpp
cppa/cppa.hpp
cppa/cppa_fwd.hpp
cppa/deserializer.hpp
cppa/detail/abstract_actor.hpp
cppa/detail/abstract_scheduled_actor.hpp
cppa/detail/abstract_tuple.hpp
cppa/detail/actor_count.hpp
cppa/detail/actor_registry.hpp
cppa/detail/atom_val.hpp
cppa/detail/behavior_impl.hpp
......@@ -56,7 +53,7 @@ cppa/detail/scheduled_actor_dummy.hpp
cppa/detail/serialize_tuple.hpp
cppa/detail/singleton_manager.hpp
cppa/detail/singleton_mixin.hpp
cppa/detail/stacked_actor_mixin.hpp
cppa/stacked.hpp
cppa/detail/swap_bytes.hpp
cppa/detail/tdata.hpp
cppa/detail/thread_pool_scheduler.hpp
......@@ -72,7 +69,7 @@ cppa/detail/uniform_type_info_map.hpp
cppa/detail/value_guard.hpp
cppa/detail/yield_interface.hpp
cppa/either.hpp
cppa/enable_weak_ptr_mixin.hpp
cppa/enable_weak_ptr.hpp
cppa/event_based_actor.hpp
cppa/exception.hpp
cppa/exit_reason.hpp
......@@ -197,11 +194,9 @@ examples/type_system/announce_2.cpp
examples/type_system/announce_3.cpp
examples/type_system/announce_4.cpp
examples/type_system/announce_5.cpp
src/abstract_scheduled_actor.cpp
src/abstract_tuple.cpp
src/actor.cpp
src/actor_addressing.cpp
src/actor_count.cpp
src/actor_proxy.cpp
src/actor_registry.cpp
src/any_tuple.cpp
......@@ -288,8 +283,11 @@ unit_testing/test_tuple.cpp
unit_testing/test_metaprogramming.cpp
unit_testing/test_uniform_type.cpp
unit_testing/test_yield_interface.cpp
cppa/memory_cached_mixin.hpp
unit_testing/test.cpp
src/exit_reason.cpp
src/on.cpp
cppa/spawn_options.hpp
cppa/extend.hpp
cppa/detail/sync_request_bouncer.hpp
cppa/memory_cached.hpp
cppa/singletons.hpp
......@@ -31,7 +31,10 @@
#ifndef CPPA_ACTOR_HPP
#define CPPA_ACTOR_HPP
#include <mutex>
#include <atomic>
#include <memory>
#include <vector>
#include <cstdint>
#include <type_traits>
......@@ -40,11 +43,15 @@
#include "cppa/cppa_fwd.hpp"
#include "cppa/attachable.hpp"
#include "cppa/message_id.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/intrusive_ptr.hpp"
#include "cppa/util/rm_ref.hpp"
namespace cppa {
class actor;
class self_type;
class serializer;
class deserializer;
......@@ -54,6 +61,12 @@ class deserializer;
*/
typedef std::uint32_t actor_id;
/**
* @brief A smart pointer type that manages instances of {@link actor}.
* @relates actor
*/
typedef intrusive_ptr<actor> actor_ptr;
/**
* @brief Base class for all actor implementations.
*/
......@@ -66,13 +79,15 @@ class actor : public channel {
* this actor is an scheduled actor that successfully changed
* its state to @p pending in response to the enqueue operation.
*/
virtual bool chained_enqueue(actor* sender, any_tuple msg);
virtual bool chained_enqueue(const actor_ptr& sender, any_tuple msg);
/**
* @brief Enqueues @p msg as a synchronous message to this actor's mailbox.
* @pre <tt>id.valid()</tt>
*/
virtual void sync_enqueue(actor* sender, message_id_t id, any_tuple msg) = 0;
virtual void sync_enqueue(const actor_ptr& sender,
message_id_t id,
any_tuple msg) = 0;
/**
* @brief Enqueues @p msg as a reply to @p request_id
......@@ -80,7 +95,7 @@ class actor : public channel {
* this actor is an scheduled actor that successfully changed
* its state to @p pending in response to the enqueue operation.
*/
virtual bool chained_sync_enqueue(actor* sender,
virtual bool chained_sync_enqueue(const actor_ptr& sender,
message_id_t id,
any_tuple msg);
......@@ -95,46 +110,40 @@ class actor : public channel {
* otherwise (actor already exited) @p false.
* @warning The actor takes ownership of @p ptr.
*/
virtual bool attach(attachable* ptr) = 0;
virtual bool attach(attachable_ptr ptr);
/**
* @brief Convenience function that attaches the functor
* or function @p ftor to this actor.
* or function @p f to this actor.
*
* The actor executes <tt>ftor()</tt> on exit, or immediatley
* The actor executes <tt>f()</tt> on exit, or immediatley
* if it already finished execution.
* @param ftor A functor, function or lambda expression that's executed
* @param f A functor, function or lambda expression that's executed
* if the actor finishes execution.
* @returns @c true if @p ftor was successfully attached to the actor;
* @returns @c true if @p f was successfully attached to the actor;
* otherwise (actor already exited) @p false.
*/
template<typename F>
bool attach_functor(F&& ftor);
bool attach_functor(F&& f);
/**
* @brief Detaches the first attached object that matches @p what.
*/
virtual void detach(const attachable::token& what) = 0;
template<typename T>
bool attach(std::unique_ptr<T>&& ptr,
typename std::enable_if<
std::is_base_of<attachable,T>::value
>::type* = 0);
virtual void detach(const attachable::token& what);
/**
* @brief Links this actor to @p other.
* @param other Actor instance that whose execution is coupled to the
* execution of this Actor.
*/
virtual void link_to(const intrusive_ptr<actor>& other) = 0;
virtual void link_to(const actor_ptr& other);
/**
* @brief Unlinks this actor from @p other.
* @param other Linked Actor.
* @note Links are automatically removed if the Actor finishes execution.
* @note Links are automatically removed if the actor finishes execution.
*/
virtual void unlink_from(const intrusive_ptr<actor>& other) = 0;
virtual void unlink_from(const actor_ptr& other);
/**
* @brief Establishes a link relation between this actor and @p other.
......@@ -142,7 +151,7 @@ class actor : public channel {
* @returns @c true if this actor is running and added @p other to its
* list of linked actors; otherwise @c false.
*/
virtual bool establish_backlink(const intrusive_ptr<actor>& other) = 0;
virtual bool establish_backlink(const actor_ptr& other);
/**
* @brief Removes a link relation between this actor and @p other.
......@@ -150,7 +159,7 @@ class actor : public channel {
* @returns @c true if this actor is running and removed @p other
* from its list of linked actors; otherwise @c false.
*/
virtual bool remove_backlink(const intrusive_ptr<actor>& other) = 0;
virtual bool remove_backlink(const actor_ptr& other);
/**
* @brief Gets an integer value that uniquely identifies this Actor in
......@@ -173,23 +182,59 @@ class actor : public channel {
actor(actor_id aid);
/**
* @brief Should be overridden by subtypes and called upon termination.
* @note Default implementation sets 'exit_reason' accordingly.
* @note Overridden functions should always call super::cleanup().
*/
virtual void cleanup(std::uint32_t reason);
/**
* @brief The default implementation for {@link link_to()}.
*/
bool link_to_impl(const actor_ptr& other);
/**
* @brief The default implementation for {@link unlink_from()}.
*/
bool unlink_from_impl(const actor_ptr& 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>.
*/
inline bool exited() const;
private:
actor_id m_id;
bool m_is_proxy;
// cannot be changed after construction
const actor_id m_id;
};
// you're either a proxy or you're not
const bool m_is_proxy;
/**
* @brief A smart pointer type that manages instances of {@link actor}.
* @relates actor
*/
typedef intrusive_ptr<actor> actor_ptr;
// initially exit_reason::not_exited
std::atomic<std::uint32_t> m_exit_reason;
class self_type;
// guards access to m_exit_reason, m_attachables, and m_links
std::mutex m_mtx;
// links to other actors
std::vector<actor_ptr> m_links;
// attached functors that are executed on cleanup
std::vector<attachable_ptr> m_attachables;
};
bool operator==(const actor_ptr& lhs, const self_type& rhs);
bool operator!=(const self_type& lhs, const actor_ptr& rhs);
// undocumented, because self_type is hidden in documentation
bool operator==(const actor_ptr&, const self_type&);
bool operator!=(const self_type&, const actor_ptr&);
/******************************************************************************
* inline and template member function implementations *
......@@ -203,39 +248,33 @@ inline bool actor::is_proxy() const {
return m_is_proxy;
}
template<typename T>
bool actor::attach(std::unique_ptr<T>&& ptr,
typename std::enable_if<
std::is_base_of<attachable,T>::value
>::type*) {
return attach(static_cast<attachable*>(ptr.release()));
inline std::uint32_t actor::exit_reason() const {
return m_exit_reason;
}
inline bool actor::exited() const {
return exit_reason() != exit_reason::not_exited;
}
template<class F>
class functor_attachable : public attachable {
struct functor_attachable : attachable {
F m_functor;
public:
template<class FArg>
functor_attachable(FArg&& arg) : m_functor(std::forward<FArg>(arg)) {
}
template<typename T>
inline functor_attachable(T&& arg) : m_functor(std::forward<T>(arg)) { }
void actor_exited(std::uint32_t reason) {
m_functor(reason);
}
void actor_exited(std::uint32_t reason) { m_functor(reason); }
bool matches(const attachable::token&) {
return false;
}
bool matches(const attachable::token&) { return false; }
};
template<typename F>
bool actor::attach_functor(F&& ftor) {
bool actor::attach_functor(F&& f) {
typedef typename util::rm_ref<F>::type f_type;
return attach(new functor_attachable<f_type>(std::forward<F>(ftor)));
typedef functor_attachable<f_type> impl;
return attach(attachable_ptr{new impl(std::forward<F>(f))});
}
} // namespace cppa
......
......@@ -36,14 +36,15 @@
#include "cppa/self.hpp"
#include "cppa/actor.hpp"
#include "cppa/extend.hpp"
#include "cppa/match_expr.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/partial_function.hpp"
#include "cppa/util/shared_spinlock.hpp"
#include "cppa/util/shared_lock_guard.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/abstract_actor.hpp"
namespace cppa {
......@@ -90,7 +91,7 @@ class actor_companion_mixin : public Base {
*/
template<typename... Args>
void set_message_handler(Args&&... matchExpressions) {
m_message_handler = match_expr_concat(std::forward<Args>(matchExpressions)...);
m_message_handler = match_expr_convert(std::forward<Args>(matchExpressions)...);
}
/**
......@@ -108,7 +109,7 @@ class actor_companion_mixin : public Base {
private:
class companion : public detail::abstract_actor<local_actor> {
class companion : public local_actor {
friend class actor_companion_mixin;
typedef util::shared_spinlock lock_type;
......@@ -123,24 +124,26 @@ class actor_companion_mixin : public Base {
std::lock_guard<lock_type> guard(m_lock);
m_parent = nullptr;
}
cleanup(cppa::exit_reason::normal);
cleanup(exit_reason::normal);
}
void enqueue(cppa::actor* sender, cppa::any_tuple msg) {
cppa::util::shared_lock_guard<lock_type> guard(m_lock);
void enqueue(const actor_ptr& sender, any_tuple msg) {
util::shared_lock_guard<lock_type> guard(m_lock);
if (m_parent) {
m_parent->new_message(new_node_ptr(sender, std::move(msg)));
}
}
void sync_enqueue(cppa::actor* sender, cppa::message_id_t id, cppa::any_tuple msg) {
cppa::util::shared_lock_guard<lock_type> guard(m_lock);
void sync_enqueue(const actor_ptr& sender,
message_id_t id,
any_tuple msg) {
util::shared_lock_guard<lock_type> guard(m_lock);
if (m_parent) {
m_parent->new_message(new_node_ptr(sender, std::move(msg), id));
}
}
bool initialized() { return true; }
bool initialized() const { return true; }
void quit(std::uint32_t) {
throw std::runtime_error("ActorWidgetMixin::Gateway::exit "
......@@ -148,21 +151,21 @@ class actor_companion_mixin : public Base {
"management instead!");
}
void dequeue(cppa::behavior&) { throw_no_recv(); }
void dequeue(behavior&) { throw_no_recv(); }
void dequeue(cppa::partial_function&) { throw_no_recv(); }
void dequeue(partial_function&) { throw_no_recv(); }
void dequeue_response(cppa::behavior&, cppa::message_id_t) {
void dequeue_response(behavior&, message_id_t) {
throw_no_recv();
}
void become_waiting_for(cppa::behavior&&, cppa::message_id_t) {
void become_waiting_for(behavior&&, message_id_t) {
throw_no_become();
}
protected:
void do_become(cppa::behavior&&, bool) { throw_no_become(); }
void do_become(behavior&&, bool) { throw_no_become(); }
private:
......@@ -172,13 +175,13 @@ class actor_companion_mixin : public Base {
}
void throw_no_become() {
throw std::runtime_error("actor_companion_mixin::companion "
throw std::runtime_error("actor_companion_companion "
"does not support libcppa's "
"become() API");
}
void throw_no_recv() {
throw std::runtime_error("actor_companion_mixin::companion "
throw std::runtime_error("actor_companion_companion "
"does not support libcppa's "
"receive() API");
}
......
......@@ -32,8 +32,9 @@
#define CPPA_ACTOR_PROXY_HPP
#include "cppa/actor.hpp"
#include "cppa/extend.hpp"
#include "cppa/weak_intrusive_ptr.hpp"
#include "cppa/enable_weak_ptr_mixin.hpp"
#include "cppa/enable_weak_ptr.hpp"
#include "cppa/network/message_header.hpp"
......@@ -43,10 +44,11 @@ class actor_proxy_cache;
/**
* @brief Represents a remote actor.
* @extends actor
*/
class actor_proxy : public enable_weak_ptr_mixin<actor> {
class actor_proxy : public extend<actor,actor_proxy>::with<enable_weak_ptr> {
typedef enable_weak_ptr_mixin<actor> super;
typedef combined_type super;
public:
......
......@@ -28,21 +28,20 @@
\******************************************************************************/
#ifndef CPPA_ACTOR_COUNT_HPP
#define CPPA_ACTOR_COUNT_HPP
#ifndef CPPA_ACTOR_STATE_HPP
#define CPPA_ACTOR_STATE_HPP
#include <cstddef>
namespace cppa {
namespace cppa { namespace detail {
enum class actor_state : int {
ready,
done,
blocked,
pending,
about_to_block
};
void inc_actor_count();
void dec_actor_count();
} // namespace cppa
/*
* @pre @p expected <= 1
*/
void actor_count_wait_until(size_t expected);
#endif // CPPA_ACTOR_STATE_HPP
} } // namespace cppa::detail
#endif // CPPA_ACTOR_COUNT_HPP
......@@ -53,7 +53,7 @@ namespace cppa {
* foo(1,2)<br>
* foo_pair(3,4)
* </tt>
* @example announce_example_1.cpp
* @example announce_1.cpp
*/
/**
......@@ -62,7 +62,7 @@ namespace cppa {
* The output of this example program is:
*
* <tt>foo(1,2)</tt>
* @example announce_example_2.cpp
* @example announce_2.cpp
*/
/**
......@@ -72,7 +72,7 @@ namespace cppa {
* The output of this example program is:
*
* <tt>foo(1,2)</tt>
* @example announce_example_3.cpp
* @example announce_3.cpp
*/
/**
......@@ -81,13 +81,13 @@ namespace cppa {
* The output of this example program is:
*
* <tt>bar(foo(1,2),3)</tt>
* @example announce_example_4.cpp
* @example announce_4.cpp
*/
/**
* @brief An advanced example for @c announce implementing serialization
* for a user-defined tree data type.
* @example announce_example_5.cpp
* @example announce_5.cpp
*/
/**
......@@ -105,7 +105,7 @@ bool announce(const std::type_info& tinfo, uniform_type_info* utype);
* @brief Creates meta informations for a non-trivial member @p C.
* @param c_ptr Pointer to the non-trivial member.
* @param args "Sub-members" of @p c_ptr
* @see {@link announce_example_4.cpp announce example 4}
* @see {@link announce_4.cpp announce example 4}
* @returns A pair of @p c_ptr and the created meta informations.
*/
template<class C, class Parent, typename... Args>
......@@ -120,7 +120,7 @@ compound_member(C Parent::*c_ptr, const Args&... args) {
* via a getter returning a mutable reference.
* @param getter Member function pointer to the getter.
* @param args "Sub-members" of @p c_ptr
* @see {@link announce_example_4.cpp announce example 4}
* @see {@link announce_4.cpp announce example 4}
* @returns A pair of @p c_ptr and the created meta informations.
*/
template<class C, class Parent, typename... Args>
......@@ -136,7 +136,7 @@ compound_member(C& (Parent::*getter)(), const Args&... args) {
* @param gspair A pair of two member function pointers representing
* getter and setter.
* @param args "Sub-members" of @p c_ptr
* @see {@link announce_example_4.cpp announce example 4}
* @see {@link announce_4.cpp announce example 4}
* @returns A pair of @p c_ptr and the created meta informations.
*/
template<class Parent, typename GRes,
......
......@@ -56,7 +56,14 @@ class any_tuple {
public:
typedef cow_ptr<detail::abstract_tuple> value_ptr;
/**
* @brief A smart pointer to the internal implementation.
*/
typedef cow_ptr<detail::abstract_tuple> data_ptr;
/**
* @brief An iterator to access each element as <tt>const void*</tt>.
*/
typedef detail::abstract_tuple::const_iterator const_iterator;
/**
......@@ -125,131 +132,97 @@ class any_tuple {
/**
* @brief Returns true if <tt>size() == 0</tt>, otherwise false.
*/
inline bool empty() const { return size() == 0; }
inline bool empty() const;
/**
* @brief Returns the value at @p as instance of @p T.
*/
template<typename T>
inline const T& get_as(size_t p) const {
CPPA_REQUIRE(*(type_at(p)) == typeid(T));
return *reinterpret_cast<const T*>(at(p));
}
inline const T& get_as(size_t p) const;
/**
* @brief Returns the value at @p as mutable data_ptr& of type @p T&.
*/
template<typename T>
inline T& get_as_mutable(size_t p) {
CPPA_REQUIRE(*(type_at(p)) == typeid(T));
return *reinterpret_cast<T*>(mutable_at(p));
}
inline T& get_as_mutable(size_t p);
inline const_iterator begin() const { return m_vals->begin(); }
/**
* @brief Returns an iterator to the beginning.
*/
inline const_iterator begin() const;
inline const_iterator end() const { return m_vals->end(); }
/**
* @brief Returns an iterator to the end.
*/
inline const_iterator end() const;
/**
* @brief Returns a copy-on-write pointer to the internal data.
*/
inline data_ptr& vals();
/**
* @brief Returns a const copy-on-write pointer to the internal data.
*/
inline const data_ptr& vals() const;
inline value_ptr& vals() { return m_vals; }
inline const value_ptr& vals() const { return m_vals; }
inline const value_ptr& cvals() const { return m_vals; }
/**
* @brief Returns a const copy-on-write pointer to the internal data.
*/
inline const data_ptr& cvals() const;
inline const std::type_info* type_token() const {
return m_vals->type_token();
}
/**
* @brief Returns either <tt>&typeid(util::type_list<Ts...>)</tt>, where
* <tt>Ts...</tt> are the element types, or <tt>&typeid(void)</tt>.
*
* The type token @p &typeid(void) indicates that this tuple is dynamically
* typed, i.e., the types where not available at compile time.
*/
inline const std::type_info* type_token() const;
inline detail::tuple_impl_info impl_type() const {
return m_vals->impl_type();
}
/**
* @brief Checks whether this
*/
inline bool dynamically_typed() const;
template<typename T>
static inline any_tuple view(T&& value,
typename std::enable_if<
util::is_iterable<
typename util::rm_ref<T>::type
>::value
>::type* = 0) {
static constexpr bool can_optimize = std::is_reference<T>::value
&& !std::is_const<T>::value;
std::integral_constant<bool, can_optimize> token;
return any_tuple{container_view(std::forward<T>(value), token)};
}
static inline any_tuple view(T&& value);
template<typename T>
static inline any_tuple view(T&& value,
typename std::enable_if<
util::is_iterable<
typename util::rm_ref<T>::type
>::value == false
>::type* = 0) {
typedef typename util::rm_ref<T>::type vtype;
typedef typename detail::implicit_conversions<vtype>::type converted;
static_assert(util::is_legal_tuple_type<converted>::value,
"T is not a valid tuple type");
typedef typename std::remove_reference<T>::type plain_type;
static constexpr bool can_optimize =
std::is_same<converted, vtype>::value
&& std::is_reference<T>::value
&& !std::is_const<plain_type>::value;
std::integral_constant<bool, can_optimize> token;
return any_tuple{simple_view(std::forward<T>(value), token)};
}
inline void force_detach() { m_vals.detach(); }
inline void force_detach();
void reset();
explicit any_tuple(detail::abstract_tuple*);
template<class StaticTypeArray>
bool types_match(StaticTypeArray& arr) {
if (size() == StaticTypeArray::size) {
for (size_t i = 0; i < StaticTypeArray::size; ++i) {
if (type_at(i) != arr[i]) {
return false;
}
}
return true;
}
return false;
}
private:
value_ptr m_vals;
data_ptr m_vals;
explicit any_tuple(const data_ptr& vals);
template<typename T, typename CanOptimize>
static any_tuple view(T&& value, std::true_type, CanOptimize token);
explicit any_tuple(const value_ptr& vals);
template<typename T, typename CanOptimize>
static any_tuple view(T&& value, std::false_type, CanOptimize token);
typedef detail::abstract_tuple* abstract_ptr;
template<typename T, typename U>
static any_tuple view(std::pair<T,U> p, std::false_type);
template<typename T>
static inline abstract_ptr simple_view(T& value, std::true_type) {
return new detail::tuple_view<T>(&value);
}
static detail::abstract_tuple* simple_view(T& value, std::true_type);
template<typename First, typename Second>
static inline abstract_ptr simple_view(std::pair<First, Second>& p,
std::true_type) {
return new detail::tuple_view<First, Second>(&p.first, &p.second);
}
template<typename T, typename U>
static detail::abstract_tuple* simple_view(std::pair<T,U>& p, std::true_type);
template<typename T>
static inline abstract_ptr simple_view(T&& value, std::false_type) {
typedef typename util::rm_ref<T>::type vtype;
typedef typename detail::implicit_conversions<vtype>::type converted;
return new detail::tuple_vals<converted>(std::forward<T>(value));
}
template<typename First, typename Second>
static inline any_tuple view(std::pair<First, Second> p, std::false_type) {
return new detail::tuple_vals<First, Second>(std::move(p.first),
std::move(p.second));
}
static detail::abstract_tuple* simple_view(T&& value, std::false_type);
template<typename T>
static inline abstract_ptr container_view(T& value, std::true_type) {
return new detail::container_tuple_view<T>(&value);
}
static detail::abstract_tuple* container_view(T& value, std::true_type);
template<typename T>
static inline abstract_ptr container_view(T&& value, std::false_type) {
typedef typename util::rm_ref<T>::type ctype;
auto cptr = new ctype(std::forward<T>(value));
return new detail::container_tuple_view<T>(cptr, true);
}
static detail::abstract_tuple* container_view(T&& value, std::false_type);
};
......@@ -276,6 +249,138 @@ inline any_tuple make_any_tuple(Args&&... args) {
return make_cow_tuple(std::forward<Args>(args)...);
}
/******************************************************************************
* inline and template member function implementations *
******************************************************************************/
inline bool any_tuple::empty() const {
return size() == 0;
}
template<typename T>
inline const T& any_tuple::get_as(size_t p) const {
CPPA_REQUIRE(*(type_at(p)) == typeid(T));
return *reinterpret_cast<const T*>(at(p));
}
template<typename T>
inline T& any_tuple::get_as_mutable(size_t p) {
CPPA_REQUIRE(*(type_at(p)) == typeid(T));
return *reinterpret_cast<T*>(mutable_at(p));
}
inline any_tuple::const_iterator any_tuple::begin() const {
return m_vals->begin();
}
inline any_tuple::const_iterator any_tuple::end() const {
return m_vals->end();
}
inline any_tuple::data_ptr& any_tuple::vals() {
return m_vals;
}
inline const any_tuple::data_ptr& any_tuple::vals() const {
return m_vals;
}
inline const any_tuple::data_ptr& any_tuple::cvals() const {
return m_vals;
}
inline const std::type_info* any_tuple::type_token() const {
return m_vals->type_token();
}
inline bool any_tuple::dynamically_typed() const {
return m_vals->dynamically_typed();
}
inline void any_tuple::force_detach() {
m_vals.detach();
}
template<typename T, bool IsIterable = true>
struct any_tuple_view_trait_impl {
static constexpr bool is_mutable_ref = std::is_reference<T>::value
&& !std::is_const<T>::value;
typedef std::integral_constant<bool,is_mutable_ref> can_optimize;
typedef std::integral_constant<bool,true> is_container;
};
template<typename T>
struct any_tuple_view_trait_impl<T,false> {
typedef typename util::rm_ref<T>::type type;
typedef typename detail::implicit_conversions<type>::type mapped;
static_assert(util::is_legal_tuple_type<mapped>::value,
"T is not a valid tuple type");
static constexpr bool is_mutable_ref = std::is_same<mapped,type>::value
&& std::is_reference<T>::value
&& not std::is_const<T>::value;
typedef std::integral_constant<bool,is_mutable_ref> can_optimize;
typedef std::integral_constant<bool,false> is_container;
};
template<typename T>
struct any_tuple_view_trait {
typedef typename util::rm_ref<T>::type type;
typedef any_tuple_view_trait_impl<T,util::is_iterable<type>::value> impl;
typedef typename impl::can_optimize can_optimize;
typedef typename impl::is_container is_container;
};
template<typename T>
inline any_tuple any_tuple::view(T&& value) {
typedef any_tuple_view_trait<T> trait;
typename trait::can_optimize can_optimize;
typename trait::is_container is_container;
return view(std::forward<T>(value), is_container, can_optimize);
}
template<typename T, typename CanOptimize>
any_tuple any_tuple::view(T&& v, std::true_type, CanOptimize t) {
return any_tuple{container_view(std::forward<T>(v), t)};
}
template<typename T, typename CanOptimize>
any_tuple any_tuple::view(T&& v, std::false_type, CanOptimize t) {
return any_tuple{simple_view(std::forward<T>(v), t)};
}
template<typename T>
detail::abstract_tuple* any_tuple::simple_view(T& v, std::true_type) {
return new detail::tuple_view<T>(&v);
}
template<typename T, typename U>
detail::abstract_tuple* any_tuple::simple_view(std::pair<T,U>& p, std::true_type) {
return new detail::tuple_view<T,U>(&p.first, &p.second);
}
template<typename T>
detail::abstract_tuple* any_tuple::simple_view(T&& v, std::false_type) {
typedef typename util::rm_ref<T>::type vtype;
typedef typename detail::implicit_conversions<vtype>::type converted;
return new detail::tuple_vals<converted>(std::forward<T>(v));
}
template<typename T, typename U>
any_tuple any_tuple::view(std::pair<T,U> p, std::false_type) {
return new detail::tuple_vals<T,U>(std::move(p.first), std::move(p.second));
}
template<typename T>
detail::abstract_tuple* any_tuple::container_view(T& v, std::true_type) {
return new detail::container_tuple_view<T>(&v);
}
template<typename T>
detail::abstract_tuple* any_tuple::container_view(T&& v, std::false_type) {
auto vptr = new typename util::rm_ref<T>::type(std::forward<T>(v));
return new detail::container_tuple_view<T>(vptr, true);
}
} // namespace cppa
#endif // CPPA_ANY_TUPLE_HPP
......@@ -31,6 +31,7 @@
#ifndef CPPA_ATTACHABLE_HPP
#define CPPA_ATTACHABLE_HPP
#include <memory>
#include <cstdint>
#include <typeinfo>
......@@ -54,17 +55,19 @@ class attachable {
* @brief Represents a pointer to a value with its RTTI.
*/
struct token {
/**
* @brief Denotes the type of @c ptr.
*/
const std::type_info& subtype;
/**
* @brief Any value, used to identify @c attachable instances.
*/
const void* ptr;
inline token(const std::type_info& msubtype, const void* mptr)
: subtype(msubtype), ptr(mptr) {
}
token(const std::type_info& subtype, const void* ptr);
};
virtual ~attachable();
......@@ -86,6 +89,12 @@ class attachable {
};
/**
* @brief A managed {@link attachable} pointer.
* @relates attachable
*/
typedef std::unique_ptr<attachable> attachable_ptr;
} // namespace cppa
#endif // CPPA_ATTACHABLE_HPP
......@@ -42,7 +42,6 @@
#include "cppa/util/if_else.hpp"
#include "cppa/util/duration.hpp"
#include "cppa/util/type_list.hpp"
#include "cppa/util/disjunction.hpp"
namespace cppa {
......@@ -59,6 +58,8 @@ class behavior {
typedef intrusive_ptr<detail::behavior_impl> impl_ptr;
static constexpr bool may_have_timeout = true;
behavior() = default;
behavior(behavior&&) = default;
behavior(const behavior&) = default;
......@@ -85,7 +86,7 @@ class behavior {
template<typename... Cases, typename Arg1, typename... Args>
behavior(const match_expr<Cases...>& arg0, const Arg1& arg1, const Args&... args)
: m_impl(match_expr_concat(arg0, arg1, args...)) { }
: m_impl(detail::match_expr_concat(arg0, arg1, args...)) { }
inline void handle_timeout() {
m_impl->handle_timeout();
......@@ -109,6 +110,8 @@ class behavior {
return {new detail::continuation_decorator<F>(std::move(fun), m_impl)};
}
const impl_ptr& as_behavior_impl() const { return m_impl; }
private:
impl_ptr m_impl;
......@@ -121,7 +124,6 @@ inline behavior operator,(const match_expr<Lhs...>& lhs,
return match_expr_convert(lhs, rhs);
}
} // namespace cppa
#endif // CPPA_BEHAVIOR_HPP
......@@ -31,6 +31,8 @@
#ifndef CPPA_CHANNEL_HPP
#define CPPA_CHANNEL_HPP
#include <type_traits>
#include "cppa/ref_counted.hpp"
#include "cppa/intrusive_ptr.hpp"
......@@ -41,6 +43,8 @@ class actor;
class group;
class any_tuple;
typedef intrusive_ptr<actor> actor_ptr;
/**
* @brief Interface for all message receivers.
*
......@@ -57,7 +61,7 @@ class channel : public ref_counted {
/**
* @brief Enqueues @p msg to the list of received messages.
*/
virtual void enqueue(actor* sender, any_tuple msg) = 0;
virtual void enqueue(const actor_ptr& sender, any_tuple msg) = 0;
protected:
......@@ -77,6 +81,12 @@ class channel : public ref_counted {
*/
typedef intrusive_ptr<channel> channel_ptr;
/**
* @brief Convenience alias.
*/
template<typename T>
using enable_if_channel = std::enable_if<std::is_base_of<channel,T>::value>;
} // namespace cppa
#endif // CPPA_CHANNEL_HPP
......@@ -35,49 +35,33 @@
#include "cppa/config.hpp"
#include "cppa/either.hpp"
#include "cppa/extend.hpp"
#include "cppa/stacked.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/yield_interface.hpp"
#include "cppa/detail/stacked_actor_mixin.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa {
#ifdef CPPA_DOCUMENTATION
/**
* @brief Context-switching actor implementation.
* @extends scheduled_actor
*/
class context_switching_actor : public scheduled_actor {
protected:
/**
* @brief Implements the actor's behavior.
* Reimplemented this function for a class-based actor.
* Returning from this member function will end the
* execution of the actor.
*/
virtual void run();
};
#else // CPPA_DOCUMENTATION
class context_switching_actor : public detail::stacked_actor_mixin<
context_switching_actor,
detail::abstract_scheduled_actor> {
class context_switching_actor : public extend<scheduled_actor,context_switching_actor>::with<stacked> {
friend class detail::behavior_stack;
friend class detail::receive_policy;
typedef detail::stacked_actor_mixin<
context_switching_actor,
detail::abstract_scheduled_actor> super;
typedef combined_type super;
public:
/**
* @brief Creates a context-switching actor running @p fun.
*/
context_switching_actor(std::function<void()> fun);
resume_result resume(util::fiber* from, actor_ptr& next_job); //override
......@@ -86,25 +70,15 @@ class context_switching_actor : public detail::stacked_actor_mixin<
protected:
context_switching_actor();
timeout_type init_timeout(const util::duration& rel_time);
detail::recursive_queue_node* await_message();
detail::recursive_queue_node* await_message(const timeout_type& abs_time);
private:
// required by detail::nestable_receive_policy
static const detail::receive_policy_flag receive_flag = detail::rp_nestable;
detail::recursive_queue_node* receive_node();
inline int init_timeout(const util::duration& timeout) {
// request timeout message
request_timeout(timeout);
return 0;
}
inline detail::recursive_queue_node* try_receive_node() {
return m_mailbox.try_pop();
}
inline detail::recursive_queue_node* try_receive_node(int) {
// timeout is triggered from an explicit timeout message
return receive_node();
}
// required by util::fiber
static void trampoline(void* _this);
......@@ -114,8 +88,6 @@ class context_switching_actor : public detail::stacked_actor_mixin<
};
#endif // CPPA_DOCUMENTATION
} // namespace cppa
#endif // CPPA_CONTEXT_SWITCHING_ACTOR_HPP
......@@ -170,8 +170,6 @@ struct cow_tuple_from_type_list< util::type_list<Ts...> > {
typedef cow_tuple<Ts...> type;
};
#ifdef CPPA_DOCUMENTATION
/**
* @ingroup CopyOnWrite
* @brief Gets a const-reference to the <tt>N</tt>th element of @p tup.
......@@ -180,8 +178,11 @@ struct cow_tuple_from_type_list< util::type_list<Ts...> > {
* <tt>N</tt>th element of @p tup.
* @relates cow_tuple
*/
template<size_t N, typename T>
const T& get(const cow_tuple<...>& tup);
template<size_t N, typename... Ts>
const typename util::at<N, Ts...>::type& get(const cow_tuple<Ts...>& tup) {
typedef typename util::at<N, Ts...>::type result_type;
return *reinterpret_cast<const result_type*>(tup.at(N));
}
/**
* @ingroup CopyOnWrite
......@@ -192,8 +193,11 @@ const T& get(const cow_tuple<...>& tup);
* @note Detaches @p tup if there are two or more references to the cow_tuple data.
* @relates cow_tuple
*/
template<size_t N, typename T>
T& get_ref(cow_tuple<...>& tup);
template<size_t N, typename... Ts>
typename util::at<N, Ts...>::type& get_ref(cow_tuple<Ts...>& tup) {
typedef typename util::at<N, Ts...>::type result_type;
return *reinterpret_cast<result_type*>(tup.mutable_at(N));
}
/**
* @ingroup ImplicitConversion
......@@ -202,31 +206,12 @@ T& get_ref(cow_tuple<...>& tup);
* @returns A cow_tuple object containing the values @p args.
* @relates cow_tuple
*/
template<typename... Args>
cow_tuple<Args...> make_cow_tuple(Args&&... args);
#else
template<size_t N, typename... Ts>
const typename util::at<N, Ts...>::type& get(const cow_tuple<Ts...>& tup) {
typedef typename util::at<N, Ts...>::type result_type;
return *reinterpret_cast<const result_type*>(tup.at(N));
}
template<size_t N, typename... Ts>
typename util::at<N, Ts...>::type& get_ref(cow_tuple<Ts...>& tup) {
typedef typename util::at<N, Ts...>::type result_type;
return *reinterpret_cast<result_type*>(tup.mutable_at(N));
}
template<typename... Args>
cow_tuple<typename detail::strip_and_convert<Args>::type...>
make_cow_tuple(Args&&... args) {
return {std::forward<Args>(args)...};
}
#endif
/**
* @brief Compares two cow_tuples.
* @param lhs First cow_tuple object.
......
......@@ -53,6 +53,7 @@
#include "cppa/any_tuple.hpp"
#include "cppa/cow_tuple.hpp"
#include "cppa/tuple_cast.hpp"
#include "cppa/singletons.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/spawn_options.hpp"
......@@ -65,8 +66,8 @@
#include "cppa/network/acceptor.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/get_behavior.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/receive_loop_helper.hpp"
/**
......@@ -119,11 +120,11 @@
*
* @section IntroHelloWorld Hello World Example
*
* @include hello_world_example.cpp
* @include hello_world.cpp
*
* @section IntroMoreExamples More Examples
*
* The {@link math_actor_example.cpp Math Actor Example} shows the usage
* The {@link math_actor.cpp Math Actor Example} shows the usage
* of {@link receive_loop} and {@link cppa::arg_match arg_match}.
* The {@link dining_philosophers.cpp Dining Philosophers Example}
* introduces event-based actors and includes a lot of <tt>libcppa</tt>
......@@ -389,13 +390,13 @@
/**
* @brief A trivial example program.
* @example hello_world_example.cpp
* @example hello_world.cpp
*/
/**
* @brief Shows the usage of {@link cppa::atom atoms}
* and {@link cppa::arg_match arg_match}.
* @example math_actor_example.cpp
* @example math_actor.cpp
*/
/**
......@@ -605,8 +606,9 @@ inline void reply_to(const response_handle& handle, Ts&&... what) {
}
/**
* @brief Sends a message to the sender of the last received message.
* @param what Message content as a tuple.
* @brief Replies with @p what to @p handle.
* @param handle Identifies a previously received request.
* @param what Response message.
*/
inline void reply_tuple_to(const response_handle& handle, any_tuple what) {
handle.apply(std::move(what));
......@@ -683,7 +685,6 @@ inline void delayed_reply(const std::chrono::duration<Rep, Period>& rtime,
/** @} */
#ifndef CPPA_DOCUMENTATION
// matches "send(this, ...)" and "send(self, ...)"
inline void send_tuple(local_actor* whom, any_tuple what) {
detail::send_impl(whom, std::move(what));
......@@ -701,7 +702,6 @@ inline actor_ptr eval_sopts(spawn_options opts, actor_ptr ptr) {
if (has_link_flag(opts)) self->link_to(ptr);
return std::move(ptr);
}
#endif // CPPA_DOCUMENTATION
/**
* @ingroup ActorCreation
......@@ -780,7 +780,8 @@ actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) {
* @warning Do not call this function in cooperatively scheduled actors.
*/
inline void await_all_others_done() {
detail::actor_count_wait_until((self.unchecked() == nullptr) ? 0 : 1);
auto value = (self.unchecked() == nullptr) ? 0 : 1;
get_actor_registry()->await_running_count_equal(value);
}
/**
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 3 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_ABSTRACT_ACTOR_HPP
#define CPPA_ABSTRACT_ACTOR_HPP
#include "cppa/config.hpp"
#include <list>
#include <mutex>
#include <atomic>
#include <vector>
#include <memory>
#include <thread>
#include <cstdint>
#include <algorithm>
#include "cppa/atom.hpp"
#include "cppa/actor.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/attachable.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/util/shared_spinlock.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa { class self_type; }
namespace cppa { namespace detail {
typedef intrusive::single_reader_queue<recursive_queue_node,disposer>
default_mailbox_impl;
struct sync_request_bouncer {
actor* ptr;
std::uint32_t rsn;
inline sync_request_bouncer(actor* p, std::uint32_t r) : ptr(p), rsn(r) { }
inline void operator()(actor* sender, const message_id_t& mid) const {
CPPA_REQUIRE(rsn != exit_reason::not_exited);
if (mid.is_request() && sender != nullptr) {
sender->sync_enqueue(ptr, mid.response_id(),
make_cow_tuple(atom("EXITED"), rsn));
}
}
inline void operator()(const recursive_queue_node& e) const {
(*this)(e.sender.get(), e.mid);
}
};
/*
* @brief Implements linking and monitoring for actors.
* @tparam Base Either {@link cppa::actor actor}
* or {@link cppa::local_actor local_actor}.
*/
template<class Base, class Mailbox = default_mailbox_impl>
class abstract_actor : public Base {
friend class ::cppa::self_type;
typedef Base super;
typedef std::lock_guard<std::mutex> guard_type;
typedef std::unique_ptr<attachable> attachable_ptr;
static constexpr bool is_local_actor = std::is_base_of<local_actor,Base>::value;
public:
typedef Mailbox mailbox_type;
typedef typename mailbox_type::value_type mailbox_element;
bool attach(attachable* ptr) { // override
if (ptr == nullptr) {
guard_type guard(m_mtx);
return m_exit_reason.load() == exit_reason::not_exited;
}
else {
attachable_ptr uptr(ptr);
std::uint32_t reason;
{ // lifetime scope of guard
guard_type guard(m_mtx);
reason = m_exit_reason.load();
if (reason == exit_reason::not_exited) {
m_attachables.push_back(std::move(uptr));
return true;
}
}
uptr->actor_exited(reason);
return false;
}
}
void detach(const attachable::token& what) { // override
attachable_ptr uptr;
{ // lifetime scope of guard
guard_type guard(m_mtx);
auto end = m_attachables.end();
auto i = std::find_if(
m_attachables.begin(), end,
[&](attachable_ptr& p) { return p->matches(what); });
if (i != end) {
uptr = std::move(*i);
m_attachables.erase(i);
}
}
// uptr will be destroyed here, without locked mutex
}
void link_to(const intrusive_ptr<actor>& other) {
(void) link_to_impl(other);
}
void unlink_from(const intrusive_ptr<actor>& other) {
(void) unlink_from_impl(other);
}
bool remove_backlink(const intrusive_ptr<actor>& other) {
if (other && other != this) {
guard_type guard(m_mtx);
auto i = std::find(m_links.begin(), m_links.end(), other);
if (i != m_links.end()) {
m_links.erase(i);
return true;
}
}
return false;
}
bool establish_backlink(const intrusive_ptr<actor>& other) {
std::uint32_t reason = exit_reason::not_exited;
if (other && other != this) {
guard_type guard(m_mtx);
reason = m_exit_reason.load();
if (reason == exit_reason::not_exited) {
auto i = std::find(m_links.begin(), m_links.end(), other);
if (i == m_links.end()) {
m_links.push_back(other);
return true;
}
}
}
// send exit message without lock
if (reason != exit_reason::not_exited) {
other->enqueue(this, make_cow_tuple(atom("EXIT"), reason));
}
return false;
}
~abstract_actor() {
if (!m_mailbox.closed()) {
auto rsn = m_exit_reason.load();
if (rsn == exit_reason::not_exited) rsn = exit_reason::normal;
sync_request_bouncer f{this, rsn};
m_mailbox.close(f);
}
}
protected:
mailbox_type m_mailbox;
inline mailbox_element* fetch_node(actor* sender,
any_tuple msg,
message_id_t id = message_id_t()) {
return memory::create<mailbox_element>(sender, std::move(msg), id);
//result->reset(sender, std::move(msg), id);
//return result;
}
template<typename... Args>
abstract_actor(Args&&... args)
: super(std::forward<Args>(args)...)
, m_exit_reason(exit_reason::not_exited){ }
inline void base_cleanup(std::true_type) {
this->m_subscriptions.clear();
}
inline void base_cleanup(std::false_type) { }
void cleanup(std::uint32_t reason) {
if (reason == exit_reason::not_exited) return;
std::integral_constant<bool,is_local_actor> token;
base_cleanup(token);
decltype(m_links) mlinks;
decltype(m_attachables) mattachables;
{ // lifetime scope of guard
guard_type guard(m_mtx);
if (m_exit_reason != exit_reason::not_exited) {
// already exited
return;
}
m_exit_reason = reason;
mlinks = std::move(m_links);
mattachables = std::move(m_attachables);
// make sure lists are definitely empty
m_links.clear();
m_attachables.clear();
}
// send exit messages
for (actor_ptr& aptr : mlinks) {
aptr->enqueue(this, make_cow_tuple(atom("EXIT"), reason));
}
for (attachable_ptr& ptr : mattachables) {
ptr->actor_exited(reason);
}
std::atomic_thread_fence(std::memory_order_seq_cst);
sync_request_bouncer f{this, reason};
m_mailbox.close(f);
}
bool link_to_impl(const intrusive_ptr<actor>& other) {
if (other && other != this) {
guard_type guard(m_mtx);
// send exit message if already exited
if (exited()) {
other->enqueue(this, make_cow_tuple(atom("EXIT"),
m_exit_reason.load()));
}
// add link if not already linked to other
// (checked by establish_backlink)
else if (other->establish_backlink(this)) {
m_links.push_back(other);
return true;
}
}
return false;
}
bool unlink_from_impl(const intrusive_ptr<actor>& other) {
guard_type guard(m_mtx);
// remove_backlink returns true if this actor is linked to other
if (other && !exited() && other->remove_backlink(this)) {
auto i = std::find(m_links.begin(), m_links.end(), other);
CPPA_REQUIRE(i != m_links.end());
m_links.erase(i);
return true;
}
return false;
}
std::uint32_t exit_reason() const { return m_exit_reason.load(); }
// @pre m_mtx.locked()
bool exited() const {
return m_exit_reason.load() != exit_reason::not_exited;
}
private:
// true if the associated thread has finished execution
std::atomic<std::uint32_t> m_exit_reason;
// guards access to m_exited, m_subscriptions, and m_links
std::mutex m_mtx;
// links to other actors
std::vector<actor_ptr> m_links;
// code that is executed on cleanup
std::vector<attachable_ptr> m_attachables;
};
} } // namespace cppa::detail
#endif // CPPA_ABSTRACT_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 3 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_SCHEDULED_ACTOR_HPP
#define CPPA_SCHEDULED_ACTOR_HPP
#include <iostream>
#include <atomic>
#include "cppa/any_tuple.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/detail/abstract_actor.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/util/fiber.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa { namespace detail {
// A spawned, scheduled Actor.
class abstract_scheduled_actor : public abstract_actor<scheduled_actor> {
typedef abstract_actor<scheduled_actor> super;
protected:
std::atomic<int> m_state;
inline bool has_pending_timeout() const {
return m_has_pending_timeout_request;
}
void request_timeout(const util::duration& d);
inline void reset_timeout() {
if (m_has_pending_timeout_request) {
++m_active_timeout_id;
m_has_pending_timeout_request = false;
}
}
inline void handle_timeout(behavior& bhvr) {
bhvr.handle_timeout();
reset_timeout();
}
inline void push_timeout() {
++m_active_timeout_id;
}
inline void pop_timeout() {
CPPA_REQUIRE(m_active_timeout_id > 0);
--m_active_timeout_id;
}
inline bool waits_for_timeout(std::uint32_t timeout_id) {
return m_has_pending_timeout_request
&& m_active_timeout_id == timeout_id;
}
bool m_has_pending_timeout_request;
std::uint32_t m_active_timeout_id;
public:
static constexpr int ready = 0x00;
static constexpr int done = 0x01;
static constexpr int blocked = 0x02;
static constexpr int pending = 0x03;
static constexpr int about_to_block = 0x04;
abstract_scheduled_actor(int state = done);
bool chained_enqueue(actor* sender, any_tuple msg);
bool chained_sync_enqueue(actor* sender, message_id_t id, any_tuple msg);
void quit(std::uint32_t reason = exit_reason::normal);
void enqueue(actor* sender, any_tuple msg);
void sync_enqueue(actor* sender, message_id_t id, any_tuple msg);
int compare_exchange_state(int expected, int new_value);
private:
bool enqueue_node(recursive_queue_node* node,
int next_state = ready,
bool* failed = nullptr);
};
} } // namespace cppa::detail
#endif // CPPA_SCHEDULED_ACTOR_HPP
......@@ -44,18 +44,11 @@
namespace cppa { namespace detail {
enum tuple_impl_info {
statically_typed,
dynamically_typed
};
class abstract_tuple : public ref_counted {
tuple_impl_info m_impl_type;
public:
inline abstract_tuple(tuple_impl_info tii) : m_impl_type(tii) { }
abstract_tuple(bool dynamically_typed);
abstract_tuple(const abstract_tuple& other);
// mutators
......@@ -76,7 +69,7 @@ class abstract_tuple : public ref_counted {
// A statically typed tuple implementation can use some optimizations,
// e.g., "impl_type() == statically_typed" implies that type_token()
// identifies all possible instances of a given tuple implementation
inline tuple_impl_info impl_type() const { return m_impl_type; }
inline bool dynamically_typed() const { return m_is_dynamic; }
// uniquely identifies this category (element types) of messages
// override this member function only if impl_type() == statically_typed
......@@ -93,6 +86,10 @@ class abstract_tuple : public ref_counted {
inline const_iterator end() const { return {this, size()}; }
inline const_iterator cend() const { return {this, size()}; }
private:
bool m_is_dynamic;
};
struct full_eq_type {
......
......@@ -195,6 +195,8 @@ class continuation_decorator : public behavior_impl {
};
typedef intrusive_ptr<behavior_impl> behavior_impl_ptr;
} } // namespace cppa::detail
#endif // BEHAVIOR_IMPL_HPP
......@@ -122,7 +122,7 @@ class behavior_stack
template<class Policy, class Client>
void exec(Policy& policy, Client* client) {
while (!empty()) {
invoke(policy, client, client->receive_node());
invoke(policy, client, client->await_message());
cleanup();
}
}
......@@ -140,7 +140,7 @@ class behavior_stack
}
}
inline void erase_at(std::vector<element_type>::reverse_iterator i) {
inline void rerase_at(std::vector<element_type>::reverse_iterator i) {
// base iterator points to the element *after* the correct element
if (i != m_elements.rend()) erase_at(i.base() - 1);
}
......@@ -152,7 +152,7 @@ class behavior_stack
template<typename UnaryPredicate>
inline void rerase_if(UnaryPredicate p) {
erase_at(std::find_if(m_elements.rbegin(), m_elements.rend(), p));
rerase_at(std::find_if(m_elements.rbegin(), m_elements.rend(), p));
}
};
......
......@@ -50,7 +50,7 @@ class container_tuple_view : public abstract_tuple {
typedef typename Container::value_type value_type;
container_tuple_view(Container* c, bool take_ownership = false)
: super(tuple_impl_info::dynamically_typed), m_ptr(c) {
: super(true), m_ptr(c) {
CPPA_REQUIRE(c != nullptr);
if (!take_ownership) m_ptr.get_deleter().disable();
}
......
......@@ -105,7 +105,7 @@ class decorated_tuple : public abstract_tuple {
vector_type m_mapping;
decorated_tuple(cow_pointer_type d, const vector_type& v)
: super(tuple_impl_info::statically_typed)
: super(false)
, m_decorated(std::move(d)), m_mapping(v) {
# ifdef CPPA_DEBUG
const cow_pointer_type& ptr = m_decorated; // prevent detaching
......@@ -116,7 +116,7 @@ class decorated_tuple : public abstract_tuple {
}
decorated_tuple(cow_pointer_type d, size_t offset)
: super(tuple_impl_info::statically_typed), m_decorated(std::move(d)) {
: super(false), m_decorated(std::move(d)) {
# ifdef CPPA_DEBUG
const cow_pointer_type& ptr = m_decorated; // prevent detaching
# endif
......
......@@ -46,7 +46,7 @@ struct matcher;
template<class Tuple, typename... T>
struct matcher<wildcard_position::nil, Tuple, T...> {
static inline bool tmatch(const Tuple& tup) {
if (tup.impl_type() == tuple_impl_info::statically_typed) {
if (not tup.dynamically_typed()) {
// statically typed tuples return &typeid(type_list<T...>)
// as type token
return typeid(util::type_list<T...>) == *(tup.type_token());
......
......@@ -108,7 +108,7 @@ class do_receive_helper {
template<typename... Args>
do_receive_helper(Args&&... args)
: m_bhvr(match_expr_concat(std::forward<Args>(args)...)) {
: m_bhvr(match_expr_convert(std::forward<Args>(args)...)) {
}
do_receive_helper(do_receive_helper&&) = default;
......@@ -122,21 +122,6 @@ class do_receive_helper {
sptr->dequeue(m_bhvr);
}
while (stmt() == false);
/*
if (m_bhvr.timeout().valid()) {
do {
sptr->dequeue(m_bhvr);
}
while (stmt() == false);
}
else {
partial_function& pfun = m_bhvr;
do {
sptr->dequeue(pfun);
}
while (stmt() == false);
}
*/
}
};
......
......@@ -36,6 +36,7 @@
#include <iostream>
#include <type_traits>
#include "cppa/logging.hpp"
#include "cppa/behavior.hpp"
#include "cppa/to_string.hpp"
#include "cppa/message_id.hpp"
......@@ -135,7 +136,7 @@ class receive_policy {
template<class Client, class FunOrBehavior>
inline void receive_wo_timeout(Client *client, FunOrBehavior& fun) {
if (!invoke_from_cache(client, fun)) {
while (!invoke(client, client->receive_node(), fun)) { }
while (!invoke(client, client->await_message(), fun)) { }
}
}
......@@ -152,7 +153,7 @@ class receive_policy {
else if (!invoke_from_cache(client, bhvr)) {
if (bhvr.timeout().is_zero()) {
pointer e = nullptr;
while ((e = client->try_receive_node()) != nullptr) {
while ((e = client->m_mailbox.try_pop()) != nullptr) {
CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr)) {
return; // done
......@@ -163,7 +164,7 @@ class receive_policy {
else {
auto timeout = client->init_timeout(bhvr.timeout());
pointer e = nullptr;
while ((e = client->try_receive_node(timeout)) != nullptr) {
while ((e = client->await_message(timeout)) != nullptr) {
CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr)) {
return; // done
......@@ -182,7 +183,7 @@ class receive_policy {
CPPA_REQUIRE(bhvr.timeout().is_zero() == false);
auto timeout = client->init_timeout(bhvr.timeout());
pointer e = nullptr;
while ((e = client->try_receive_node(timeout)) != nullptr) {
while ((e = client->await_message(timeout)) != nullptr) {
CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr, mid)) {
return; // done
......@@ -190,9 +191,7 @@ class receive_policy {
}
handle_timeout(client, bhvr);
}
else {
while (!invoke(client, client->receive_node(), bhvr, mid)) { }
}
else while (!invoke(client, client->await_message(), bhvr, mid)) { }
}
}
......@@ -243,9 +242,8 @@ class receive_policy {
CPPA_REQUIRE(!message_id.valid());
if (client->m_trap_exit == false) {
if (v1 != exit_reason::normal) {
// TODO: check for possible memory leak here
// ('node' might not get destroyed)
client->quit(v1);
return non_normal_exit_signal;
}
return normal_exit_signal;
}
......@@ -327,7 +325,7 @@ class receive_policy {
if (client->has_behavior()) {
client->request_timeout(client->get_behavior().timeout());
}
else client->m_has_pending_timeout_request = false;
else client->reset_timeout();
}
template<class Client>
......@@ -350,12 +348,25 @@ class receive_policy {
}
switch (this->filter_msg(client, node)) {
default: {
// one of:
// - normal_exit_signal
// - expired_sync_response
// - expired_timeout_message
CPPA_CRITICAL("illegal filter result");
}
case normal_exit_signal: {
CPPA_LOG_DEBUG("dropped message: normal exit signal");
return hm_drop_msg;
}
case expired_sync_response: {
CPPA_LOG_DEBUG("dropped message: expired sync response");
return hm_drop_msg;
}
case expired_timeout_message: {
CPPA_LOG_DEBUG("dropped message: expired timeout message");
return hm_drop_msg;
}
case non_normal_exit_signal: {
// this message was handled
// by calling client->quit(...)
return hm_msg_handled;
}
case timeout_message: {
handle_timeout(client, fun);
if (awaited_response.valid()) {
......@@ -372,6 +383,7 @@ class receive_policy {
if (awaited_response.valid() && node->mid == awaited_response) {
auto previous_node = hm_begin(client, node, policy);
if (!fun(node->msg) && handle_sync_failure_on_mismatch) {
CPPA_LOG_WARNING("sync failure occured");
client->handle_sync_failure();
}
client->mark_arrived(awaited_response);
......@@ -390,6 +402,8 @@ class receive_policy {
auto id = node->mid;
auto sender = node->sender;
if (id.valid() && !id.is_answered() && sender) {
CPPA_LOG_WARNING("actor did not reply to a "
"synchronous request message");
sender->sync_enqueue(client,
id.response_id(),
make_any_tuple(atom("VOID")));
......
......@@ -37,14 +37,14 @@
#include "cppa/any_tuple.hpp"
#include "cppa/message_id.hpp"
#include "cppa/ref_counted.hpp"
#include "cppa/memory_cached_mixin.hpp"
#include "cppa/memory_cached.hpp"
// needs access to constructor + destructor to initialize m_dummy_node
namespace cppa { class local_actor; }
namespace cppa { namespace detail {
class recursive_queue_node : public memory_cached_mixin<memory_managed> {
class recursive_queue_node : public memory_cached<memory_managed,recursive_queue_node> {
friend class memory;
friend class ::cppa::local_actor;
......@@ -64,11 +64,18 @@ class recursive_queue_node : public memory_cached_mixin<memory_managed> {
recursive_queue_node& operator=(recursive_queue_node&&) = delete;
recursive_queue_node& operator=(const recursive_queue_node&) = delete;
template<typename... Ts>
inline static recursive_queue_node* create(Ts&&... args) {
return memory::create<recursive_queue_node>(std::forward<Ts>(args)...);
}
private:
recursive_queue_node() = default;
recursive_queue_node(actor_ptr sptr, any_tuple data, message_id_t id = message_id_t());
recursive_queue_node(const actor_ptr& sptr,
any_tuple data,
message_id_t id = message_id_t{});
};
......
......@@ -31,22 +31,19 @@
#ifndef CPPA_SCHEDULED_ACTOR_DUMMY_HPP
#define CPPA_SCHEDULED_ACTOR_DUMMY_HPP
#include "cppa/detail/abstract_scheduled_actor.hpp"
#include "cppa/scheduled_actor.hpp"
namespace cppa { namespace detail {
struct scheduled_actor_dummy : abstract_scheduled_actor {
struct scheduled_actor_dummy : scheduled_actor {
scheduled_actor_dummy();
void enqueue(const actor_ptr&, any_tuple);
void sync_enqueue(const actor_ptr&, message_id_t, any_tuple);
resume_result resume(util::fiber*, actor_ptr&);
void quit(std::uint32_t);
void dequeue(behavior&);
void dequeue(partial_function&);
void dequeue_response(behavior&, message_id_t);
void link_to(const intrusive_ptr<actor>&);
void unlink_from(const intrusive_ptr<actor>&);
bool establish_backlink(const intrusive_ptr<actor>&);
bool remove_backlink(const intrusive_ptr<actor>&);
void detach(const attachable::token&);
bool attach(attachable*);
void do_become(behavior&&, bool);
void become_waiting_for(behavior&&, message_id_t);
bool has_behavior();
......
......@@ -37,7 +37,6 @@ namespace cppa {
class logging;
class scheduler;
class msg_content;
} // namespace cppa
......@@ -82,7 +81,7 @@ class singleton_manager {
private:
/**
/*
* @brief Type @p T has to provide: <tt>static T* create_singleton()</tt>,
* <tt>void initialize()</tt>, <tt>void destroy()</tt>,
* and <tt>dispose()</tt>.
......
......@@ -28,22 +28,37 @@
\******************************************************************************/
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/singleton_manager.hpp"
#ifndef CPPA_SYNC_REQUEST_BOUNCER_HPP
#define CPPA_SYNC_REQUEST_BOUNCER_HPP
namespace cppa { namespace detail {
#include <cstdint>
void inc_actor_count() {
singleton_manager::get_actor_registry()->inc_running();
}
#include "cppa/actor.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/message_id.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
void dec_actor_count() {
singleton_manager::get_actor_registry()->dec_running();
}
namespace cppa { namespace detail {
void actor_count_wait_until(size_t value) {
singleton_manager::get_actor_registry()->await_running_count_equal(value);
}
struct sync_request_bouncer {
std::uint32_t rsn;
constexpr sync_request_bouncer(std::uint32_t r)
: rsn(r == exit_reason::not_exited ? exit_reason::normal : r) { }
inline void operator()(const actor_ptr& sender, const message_id_t& mid) const {
CPPA_REQUIRE(rsn != exit_reason::not_exited);
if (mid.is_request() && sender != nullptr) {
actor_ptr nobody;
sender->sync_enqueue(nobody,
mid.response_id(),
make_any_tuple(atom("EXITED"), rsn));
}
}
inline void operator()(const recursive_queue_node& e) const {
(*this)(e.sender.get(), e.mid);
}
};
} } // namespace cppa::detail
#endif // CPPA_SYNC_REQUEST_BOUNCER_HPP
......@@ -176,8 +176,8 @@ struct tdata<> {
inline bool operator==(const tdata&) const { return true; }
inline tuple_impl_info impl_type() const {
return statically_typed;
inline bool dynamically_typed() const {
return false;
}
};
......
......@@ -37,7 +37,6 @@
#include "cppa/context_switching_actor.hpp"
#include "cppa/util/producer_consumer_list.hpp"
#include "cppa/detail/scheduled_actor_dummy.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa { namespace detail {
......
......@@ -59,9 +59,8 @@ class tuple_vals : public abstract_tuple {
tuple_vals(const tuple_vals&) = default;
template<typename... Args>
tuple_vals(Args&&... args)
: super(tuple_impl_info::statically_typed)
, m_data(std::forward<Args>(args)...) { }
tuple_vals(Args&&... args) : super(false)
, m_data(std::forward<Args>(args)...) { }
const void* native_data() const {
return &m_data;
......
......@@ -68,9 +68,7 @@ class tuple_view : public abstract_tuple {
/**
* @warning @p tuple_view does @b NOT takes ownership for given pointers
*/
tuple_view(Ts*... args)
: super(tuple_impl_info::statically_typed), m_data(args...) {
}
tuple_view(Ts*... args) : super(false), m_data(args...) { }
inline data_type& data() {
return m_data;
......
......@@ -28,8 +28,8 @@
\******************************************************************************/
#ifndef CPPA_ENABLE_WEAK_PTR_MIXIN_HPP
#define CPPA_ENABLE_WEAK_PTR_MIXIN_HPP
#ifndef CPPA_ENABLE_WEAK_PTR_HPP
#define CPPA_ENABLE_WEAK_PTR_HPP
#include <mutex>
#include <utility>
......@@ -47,10 +47,8 @@ namespace cppa {
/**
* @brief Enables derived classes to be used in {@link weak_intrusive_ptr}.
*/
template<class Base>
class enable_weak_ptr_mixin : public Base {
typedef Base super;
template<class Base, class Subtype>
class enable_weak_ptr : public Base {
template<typename T>
friend class weak_intrusive_ptr;
......@@ -60,9 +58,11 @@ class enable_weak_ptr_mixin : public Base {
protected:
typedef enable_weak_ptr combined_type;
template<typename... Args>
enable_weak_ptr_mixin(Args&&... args)
: super(std::forward<Args>(args)...)
enable_weak_ptr(Args&&... args)
: Base(std::forward<Args>(args)...)
, m_anchor(new weak_ptr_anchor(this)) { }
void request_deletion() {
......@@ -81,4 +81,4 @@ class enable_weak_ptr_mixin : public Base {
} // namespace cppa
#endif // CPPA_ENABLE_WEAK_PTR_MIXIN_HPP
#endif // CPPA_ENABLE_WEAK_PTR_HPP
......@@ -38,23 +38,21 @@
#include "cppa/config.hpp"
#include "cppa/either.hpp"
#include "cppa/extend.hpp"
#include "cppa/behavior.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa {
#ifdef CPPA_DOCUMENTATION
/**
* @brief Base class for all event-based actor implementations.
*/
class event_based_actor : public scheduled_actor {
#else // CPPA_DOCUMENTATION
class event_based_actor : public detail::abstract_scheduled_actor {
#endif // CPPA_DOCUMENTATION
friend class detail::receive_policy;
typedef detail::abstract_scheduled_actor super;
typedef scheduled_actor super;
public:
......@@ -81,7 +79,7 @@ class event_based_actor : public detail::abstract_scheduled_actor {
*/
virtual void init() = 0;
void quit(std::uint32_t reason = exit_reason::normal);
virtual void quit(std::uint32_t reason = exit_reason::normal);
bool has_behavior();
......@@ -149,7 +147,7 @@ class event_based_actor : public detail::abstract_scheduled_actor {
static const detail::receive_policy_flag receive_flag = detail::rp_sequential;
inline void handle_timeout(behavior& bhvr) {
CPPA_REQUIRE(bhvr.timeout().valid());
m_has_pending_timeout_request = false;
reset_timeout();
bhvr.handle_timeout();
if (m_bhvr_stack.empty() == false) {
request_timeout(get_behavior().timeout());
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 3 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_MIXED_HPP
#define CPPA_MIXED_HPP
// saves some typing
#define CPPA_MIXIN template<class,class> class
namespace cppa {
namespace detail {
template<class B, class D, CPPA_MIXIN... Ms>
struct extend_helper;
template<class B, class D>
struct extend_helper<B,D> { typedef D type; };
template<class B, class D, CPPA_MIXIN M, CPPA_MIXIN... Ms>
struct extend_helper<B,D,M,Ms...> : extend_helper<B,M<D,B>,Ms...> { };
} // namespace detail
/**
* @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>.
*
* Mixins in libcppa always have two template parameters: base type and
* derived type. This allows mixins to make use of the curiously recurring
* template pattern (CRTP).
*/
template<class Base, class Derived>
struct extend {
/**
* @brief An alias for the result type.
*/
template<template<class,class> class... Mixins>
using with = typename detail::extend_helper<Derived,Base,Mixins...>::type;
};
} // namespace cppa
#endif // CPPA_MIXED_HPP
......@@ -35,7 +35,7 @@
namespace cppa { namespace factory {
#ifdef CPPA_DOCUMENTATION
void default_cleanup();
/**
* @brief Returns a factory for event-based actors using @p fun as
......@@ -57,7 +57,10 @@ namespace cppa { namespace factory {
* unless an initial value is passed to @p spawn.
*/
template<typename InitFun>
auto event_based(InitFun fun);
inline typename detail::ebaf_from_functor<InitFun, void (*)()>::type
event_based(InitFun init) {
return {std::move(init), default_cleanup};
}
/**
* @brief Returns a factory for event-based actors using @p fun0 as
......@@ -65,27 +68,12 @@ auto event_based(InitFun fun);
* and @p fun1 as implementation for
* {@link cppa::event_based_actor::on_exit() on_exit()}.
*/
template<typename InitFun, OnExitFun>
auto event_based(InitFun fun0, OnExitFun fun1);
#else // CPPA_DOCUMENTATION
void default_cleanup();
template<typename InitFun>
inline typename detail::ebaf_from_functor<InitFun, void (*)()>::type
event_based(InitFun init) {
return {std::move(init), default_cleanup};
}
template<typename InitFun, typename OnExitFun>
inline typename detail::ebaf_from_functor<InitFun, OnExitFun>::type
event_based(InitFun init, OnExitFun on_exit) {
return {std::move(init), on_exit};
}
#endif // CPPA_DOCUMENTATION
} } // namespace cppa::factory
#endif // CPPA_FACTORY_HPP
......@@ -31,9 +31,6 @@
#ifndef CPPA_GET_HPP
#define CPPA_GET_HPP
// functions are documented in the implementation headers
#ifndef CPPA_DOCUMENTATION
#include <tuple>
#include <cstddef>
......@@ -108,5 +105,4 @@ inline auto get_cv_aware(const Tuple& tup) -> decltype(get<Pos>(tup)) {
} // namespace cppa
#endif // CPPA_DOCUMENTATION
#endif // CPPA_GET_HPP
......@@ -728,11 +728,6 @@ struct mutable_gref_wrapped<T&> {
namespace placeholders {
// doxygen cannot handle anonymous namespaces
#ifndef CPPA_DOCUMENTATION
namespace {
#endif // CPPA_DOCUMENTATION
constexpr guard_placeholder<0> _x1;
constexpr guard_placeholder<1> _x2;
constexpr guard_placeholder<2> _x3;
......@@ -743,11 +738,6 @@ constexpr guard_placeholder<6> _x7;
constexpr guard_placeholder<7> _x8;
constexpr guard_placeholder<8> _x9;
// doxygen cannot handle anonymous namespaces
#ifndef CPPA_DOCUMENTATION
} // namespace <anonymous>
#endif // CPPA_DOCUMENTATION
} // namespace placeholders
} // namespace cppa
......
......@@ -36,6 +36,7 @@
#include "cppa/group.hpp"
#include "cppa/actor.hpp"
#include "cppa/extend.hpp"
#include "cppa/behavior.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/message_id.hpp"
......@@ -43,9 +44,9 @@
#include "cppa/exit_reason.hpp"
#include "cppa/response_handle.hpp"
#include "cppa/partial_function.hpp"
#include "cppa/memory_cached_mixin.hpp"
#include "cppa/util/duration.hpp"
#include "cppa/memory_cached.hpp"
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
......@@ -60,45 +61,33 @@ class sync_handle_helper;
namespace detail { class receive_policy; }
template<bool DiscardOld>
struct behavior_policy { static const bool discard_old = DiscardOld; };
template<bool DiscardBehavior>
struct behavior_policy { static constexpr bool discard_old = DiscardBehavior; };
typedef behavior_policy<false> keep_behavior_t;
typedef behavior_policy<true > discard_behavior_t;
// doxygen doesn't parse anonymous namespaces correctly
#ifndef CPPA_DOCUMENTATION
namespace {
#endif // CPPA_DOCUMENTATION
/**
* @brief Policy tag that causes {@link event_based_actor::become} to
* discard the current behavior.
* @relates local_actor
*/
constexpr discard_behavior_t discard_behavior = discard_behavior_t();
constexpr discard_behavior_t discard_behavior = discard_behavior_t{};
/**
* @brief Policy tag that causes {@link event_based_actor::become} to
* keep the current behavior available.
* @relates local_actor
*/
constexpr keep_behavior_t keep_behavior = keep_behavior_t();
#ifndef CPPA_DOCUMENTATION
} // namespace <anonymous>
#endif // CPPA_DOCUMENTATION
class message_future;
//inline sync_recv_helper receive_response(message_future);
constexpr keep_behavior_t keep_behavior = keep_behavior_t{};
/**
* @brief Base class for local running Actors.
* @extends actor
*/
class local_actor : public memory_cached_mixin<actor> {
class local_actor : public extend<actor,local_actor>::with<memory_cached> {
typedef actor super;
typedef combined_type super;
friend class scheduler;
friend class detail::memory;
......@@ -163,39 +152,29 @@ class local_actor : public memory_cached_mixin<actor> {
/**
* @brief Checks whether this actor traps exit messages.
*/
inline bool trap_exit() const {
return m_trap_exit;
}
inline bool trap_exit() const;
/**
* @brief Enables or disables trapping of exit messages.
*/
inline void trap_exit(bool new_value) {
m_trap_exit = new_value;
}
inline void trap_exit(bool new_value);
/**
* @brief Checks whether this actor uses the "chained send" optimization.
*/
inline bool chaining() const {
return m_chaining;
}
inline bool chaining() const;
/**
* @brief Enables or disables chained send.
*/
inline void chaining(bool new_value) {
m_chaining = m_is_scheduled && new_value;
}
inline void chaining(bool new_value);
/**
* @brief Returns the last message that was dequeued
* from the actor's mailbox.
* @warning Only set during callback invocation.
*/
inline any_tuple& last_dequeued() {
return m_current_node->msg;
}
inline any_tuple& last_dequeued();
/**
* @brief Returns the sender of the last dequeued message.
......@@ -203,9 +182,7 @@ class local_actor : public memory_cached_mixin<actor> {
* @note Implicitly used by the function {@link cppa::reply}.
* @see cppa::reply()
*/
inline actor_ptr& last_sender() {
return m_current_node->sender;
}
inline actor_ptr& last_sender();
/**
* @brief Adds a unidirectional @p monitor to @p whom.
......@@ -224,74 +201,37 @@ class local_actor : public memory_cached_mixin<actor> {
// become/unbecome API
/**
* @brief Sets the actor's behavior to @p bhvr and discards the
* previous behavior if the policy is {@link discard_behavior}.
* @note The recommended way of using this member function is to pass
* a pointer to a member variable.
* @warning @p bhvr is owned by the caller and must remain valid until
* the actor terminates.
*/
template<bool DiscardOld>
inline void become(behavior_policy<DiscardOld>, behavior* bhvr) {
do_become(*bhvr, DiscardOld);
}
/**
* @brief Sets the actor's behavior to @p bhvr and discards the
* previous behavior if the policy is {@link discard_behavior}.
*/
template<bool DiscardOld>
inline void become(behavior_policy<DiscardOld>, behavior bhvr) {
do_become(std::move(bhvr), true);
}
template<bool Discard>
inline void become(behavior_policy<Discard>, behavior bhvr);
/**
* @brief Sets the actor's behavior and discards the
* previous behavior if the policy is {@link discard_behavior}.
*/
template<bool DiscardOld, typename Arg0, typename Arg1, typename... Args>
inline void become(behavior_policy<DiscardOld>,
Arg0&& arg0, Arg1&& arg1, Args&&... args) {
do_become(match_expr_convert(std::forward<Arg0>(arg0),
std::forward<Arg1>(arg1),
std::forward<Args>(args)...),
DiscardOld);
}
template<bool Discard, typename T0, typename T1, typename... Ts>
inline void become(behavior_policy<Discard>, T0&& a0, T1&& a1, Ts&&... as);
/**
* @brief Sets the actor's behavior;
* equal to <tt>become(discard_old, bhvr</tt>.
*/
inline void become(behavior bhvr) {
become(discard_behavior, std::move(bhvr));
}
inline void become(behavior bhvr);
/**
* @brief Sets the actor's behavior;
* equal to <tt>become(discard_old, bhvr</tt>.
*/
inline void become(behavior* bhvr) {
become(discard_behavior, bhvr);
}
/**
* @brief Sets the actor's behavior;
* equal to <tt>become(discard_old, bhvr</tt>.
*/
template<typename... Cases, typename... Args>
inline void become(match_expr<Cases...> arg0, Args&&... args) {
do_become(match_expr_convert(std::move(arg0),
std::forward<Args>(args)...),
true);
}
template<typename... Cases, typename... Ts>
inline void become(match_expr<Cases...> a, Ts&&... as);
/**
* @brief Returns to a previous behavior if available.
*/
inline void unbecome() {
m_bhvr_stack.pop_async_back();
}
inline void unbecome();
/**
* @brief Can be overridden to initialize an actor before any
......@@ -379,7 +319,7 @@ class local_actor : public memory_cached_mixin<actor> {
local_actor(bool is_scheduled = false);
virtual bool initialized() = 0;
virtual bool initialized() const = 0;
inline bool chaining_enabled() {
return m_chaining && !m_chained_actor;
......@@ -389,7 +329,7 @@ class local_actor : public memory_cached_mixin<actor> {
whom->enqueue(this, std::move(what));
}
inline void send_message(actor* whom, any_tuple&& what) {
inline void send_message(const actor_ptr& whom, any_tuple&& what) {
if (chaining_enabled()) {
if (whom->chained_enqueue(this, std::move(what))) {
m_chained_actor = whom;
......@@ -398,7 +338,7 @@ class local_actor : public memory_cached_mixin<actor> {
else whom->enqueue(this, std::move(what));
}
inline message_id_t send_sync_message(actor* whom, any_tuple&& what) {
inline message_id_t send_sync_message(const actor_ptr& whom, any_tuple&& what) {
auto id = ++m_last_request_id;
CPPA_REQUIRE(id.is_request());
if (chaining_enabled()) {
......@@ -412,7 +352,7 @@ class local_actor : public memory_cached_mixin<actor> {
return awaited_response;
}
message_id_t send_timed_sync_message(actor* whom,
message_id_t send_timed_sync_message(const actor_ptr& whom,
const util::duration& rel_time,
any_tuple&& what);
......@@ -491,6 +431,8 @@ class local_actor : public memory_cached_mixin<actor> {
m_bhvr_stack.erase(id);
}
void cleanup(std::uint32_t reason);
private:
std::function<void()> m_sync_failure_handler;
......@@ -515,6 +457,58 @@ class local_actor : public memory_cached_mixin<actor> {
*/
typedef intrusive_ptr<local_actor> local_actor_ptr;
/******************************************************************************
* inline and template member function implementations *
******************************************************************************/
inline bool local_actor::trap_exit() const {
return m_trap_exit;
}
inline void local_actor::trap_exit(bool new_value) {
m_trap_exit = new_value;
}
inline bool local_actor::chaining() const {
return m_chaining;
}
inline void local_actor::chaining(bool new_value) {
m_chaining = m_is_scheduled && new_value;
}
inline any_tuple& local_actor::last_dequeued() {
return m_current_node->msg;
}
inline actor_ptr& local_actor::last_sender() {
return m_current_node->sender;
}
template<bool Discard>
inline void local_actor::become(behavior_policy<Discard>, behavior bhvr) {
do_become(std::move(bhvr), Discard);
}
template<bool Discard, typename T0, typename T1, typename... Ts>
inline void local_actor::become(behavior_policy<Discard>, T0&& a0, T1&& a1, Ts&&... as) {
auto bhvr = match_expr_convert(std::forward<T0>(a0), std::forward<T1>(a1), std::forward<Ts>(as)...);
do_become(std::move(bhvr), Discard);
}
inline void local_actor::become(behavior bhvr) {
do_become(std::move(bhvr), true);
}
template<typename... Cases, typename... Ts>
inline void local_actor::become(match_expr<Cases...> a, Ts&&... as) {
do_become(match_expr_convert(std::move(a), std::forward<Ts>(as)...), true);
}
inline void local_actor::unbecome() {
m_bhvr_stack.pop_async_back();
}
} // namespace cppa
#endif // CPPA_CONTEXT_HPP
......@@ -33,6 +33,7 @@
#include <sstream>
#include "cppa/singletons.hpp"
#include "cppa/detail/demangle.hpp"
/*
......@@ -65,27 +66,17 @@ class logging {
int line_num,
const std::string& msg ) = 0;
static logging* instance();
class trace_helper {
public:
inline trace_helper(std::string class_name,
const char* fun_name,
const char* file_name,
int line_num,
const std::string& msg)
: m_class(std::move(class_name)), m_fun_name(fun_name)
, m_file_name(file_name), m_line_num(line_num) {
logging::instance()->log("TRACE ", m_class.c_str(), fun_name,
file_name, line_num, "ENTRY " + msg);
}
inline ~trace_helper() {
logging::instance()->log("TRACE ", m_class.c_str(), m_fun_name,
m_file_name, m_line_num, "EXIT");
}
trace_helper(std::string class_name,
const char* fun_name,
const char* file_name,
int line_num,
const std::string& msg);
~trace_helper();
private:
......@@ -115,129 +106,119 @@ class logging {
#define CPPA_VOID_STMT static_cast<void>(0)
#ifndef CPPA_LOG_LEVEL
#define CPPA_LOG_ERROR(unused) CPPA_VOID_STMT
#define CPPA_LOG_ERROR_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOGF_ERROR(unused) CPPA_VOID_STMT
#define CPPA_LOGF_ERROR_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOG_WARNING(unused) CPPA_VOID_STMT
#define CPPA_LOG_WARNING_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOGF_WARNING(unused) CPPA_VOID_STMT
#define CPPA_LOGF_WARNING_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOG_INFO(unused) CPPA_VOID_STMT
#define CPPA_LOG_INFO_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOGF_INFO(unused) CPPA_VOID_STMT
#define CPPA_LOGF_INFO_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOG_DEBUG(unused) CPPA_VOID_STMT
#define CPPA_LOG_DEBUG_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOGF_DEBUG(unused) CPPA_VOID_STMT
#define CPPA_LOGF_DEBUG_IF(unused1,unused2) CPPA_VOID_STMT
#define CPPA_LOG_TRACE(unused) CPPA_VOID_STMT
#define CPPA_LOGF_TRACE(unused) CPPA_VOID_STMT
# define CPPA_LOG(classname, funname, level, message) CPPA_VOID_STMT
# define CPPA_LIF(stmt, logstmt) CPPA_VOID_STMT
#else
# define CPPA_LOG(classname, funname, level, message) { \
std::ostringstream scoped_oss; scoped_oss << message; \
::cppa::get_logger()->log( \
level, classname, funname, __FILE__, __LINE__, scoped_oss.str()); \
} CPPA_VOID_STMT
# define CPPA_LIF(stmt, logstmt) if (stmt) { logstmt ; } CPPA_VOID_STMT
#endif
#define CPPA_DO_LOG_FUN(level, message) { \
std::ostringstream scoped_oss; scoped_oss << message; \
::cppa::logging::instance()->log( \
level, "NONE", \
__FUNCTION__, __FILE__, __LINE__, scoped_oss.str()); \
} CPPA_VOID_STMT
#define CPPA_DO_LOG_MEMBER_FUN(level, message) { \
std::ostringstream scoped_oss; scoped_oss << message; \
::cppa::logging::instance()->log( \
level, ::cppa::detail::demangle(typeid(*this)).c_str(), \
__FUNCTION__, __FILE__, __LINE__, scoped_oss.str()); \
} CPPA_VOID_STMT
#define CPPA_CLASS_NAME ::cppa::detail::demangle(typeid(*this)).c_str()
#define CPPA_LOG_ERROR(message) CPPA_DO_LOG_MEMBER_FUN("ERROR ", message)
#define CPPA_LOGF_ERROR(message) CPPA_DO_LOG_FUN("ERROR ", message)
/**
* @brief Logs a custom error message @p msg with class name @p cname
* and function name @p fname.
*/
#define CPPA_LOGC_ERROR(cname, fname, msg) CPPA_LOG(cname, fname, "ERROR", msg)
#define CPPA_LOGC_WARNING(cname, fname, msg) CPPA_VOID_STMT
#define CPPA_LOGC_INFO(cname, fname, msg) CPPA_VOID_STMT
#define CPPA_LOGC_DEBUG(cname, fname, msg) CPPA_VOID_STMT
#define CPPA_LOGC_TRACE(cname, fname, msg) CPPA_VOID_STMT
// enable warnings
#if CPPA_LOG_LEVEL > 0
# define CPPA_LOG_WARNING(message) CPPA_DO_LOG_MEMBER_FUN("WARNING", message)
# define CPPA_LOGF_WARNING(message) CPPA_DO_LOG_FUN("WARNING", message)
#else
# define CPPA_LOG_WARNING(unused) CPPA_VOID_STMT
# define CPPA_LOGF_WARNING(unused) CPPA_VOID_STMT
# undef CPPA_LOGC_WARNING
# define CPPA_LOGC_WARNING(cname, fname, msg) CPPA_LOG(cname, fname, "WARN ", msg)
#endif
// enable info messages
#if CPPA_LOG_LEVEL > 1
# define CPPA_LOG_INFO(message) CPPA_DO_LOG_MEMBER_FUN("INFO ", message)
# define CPPA_LOGF_INFO(message) CPPA_DO_LOG_FUN("INFO ", message)
#else
# define CPPA_LOG_INFO(unused) CPPA_VOID_STMT
# define CPPA_LOGF_INFO(unused) CPPA_VOID_STMT
# undef CPPA_LOGC_INFO
# define CPPA_LOGC_INFO(cname, fname, msg) CPPA_LOG(cname, fname, "INFO ", msg)
#endif
// enable debug messages
#if CPPA_LOG_LEVEL > 2
# define CPPA_LOG_DEBUG(message) CPPA_DO_LOG_MEMBER_FUN("DEBUG ", message)
# define CPPA_LOGF_DEBUG(message) CPPA_DO_LOG_FUN("DEBUG ", message)
#else
# define CPPA_LOG_DEBUG(unused) CPPA_VOID_STMT
# define CPPA_LOGF_DEBUG(unused) CPPA_VOID_STMT
# undef CPPA_LOGC_DEBUG
# define CPPA_LOGC_DEBUG(cname, fname, msg) CPPA_LOG(cname, fname, "DEBUG", msg)
#endif
// enable trace messages
#if CPPA_LOG_LEVEL > 3
# define CPPA_RPAREN )
# define CPPA_LPAREN (
# define CPPA_GET(what) what
# define CPPA_CONCAT_I(lhs,rhs) lhs ## rhs
# define CPPA_CONCAT(lhs,rhs) CPPA_CONCAT_I(lhs,rhs)
# define CPPA_CONCATL(lhs) CPPA_CONCAT(lhs, __LINE__)
# define CPPA_LOG_TRACE(message) \
::std::ostringstream CPPA_CONCATL(cppa_trace_helper_) ; \
CPPA_CONCATL(cppa_trace_helper_) << message ; \
::cppa::logging::trace_helper CPPA_CONCATL(cppa_fun_trace_helper_) \
CPPA_LPAREN ::cppa::detail::demangle \
CPPA_LPAREN typeid CPPA_LPAREN decltype CPPA_LPAREN *this \
CPPA_RPAREN CPPA_RPAREN CPPA_RPAREN , \
__func__ , __FILE__ , __LINE__ , \
CPPA_CONCATL(cppa_trace_helper_) .str() CPPA_RPAREN
# define CPPA_LOGF_TRACE(message) \
::std::ostringstream CPPA_CONCATL(cppa_trace_helper_) ; \
CPPA_CONCATL(cppa_trace_helper_) << message ; \
::cppa::logging::trace_helper CPPA_CONCATL(cppa_fun_trace_helper_) \
CPPA_LPAREN "NONE" , \
__func__ , __FILE__ , __LINE__ , \
CPPA_CONCATL(cppa_trace_helper_) .str() CPPA_RPAREN
#else
# define CPPA_LOG_TRACE(unused)
# define CPPA_LOGF_TRACE(unused)
# undef CPPA_LOGC_TRACE
# define CPPA_CONCAT_I(lhs,rhs) lhs ## rhs
# define CPPA_CONCAT(lhs,rhs) CPPA_CONCAT_I(lhs,rhs)
# define CPPA_CONCATL(lhs) CPPA_CONCAT(lhs, __LINE__)
# define CPPA_LOGC_TRACE(cname, fname, msg) \
::std::ostringstream CPPA_CONCATL(cppa_trace_helper_) ; \
CPPA_CONCATL(cppa_trace_helper_) << msg ; \
::cppa::logging::trace_helper CPPA_CONCATL(cppa_fun_trace_helper_) { \
cname, fname , __FILE__ , __LINE__ , \
CPPA_CONCATL(cppa_trace_helper_) .str() }
#endif
#define CPPA_LOG_ERROR_IF(stmt,message) \
if (stmt) { CPPA_LOG_ERROR(message); }; CPPA_VOID_STMT
#define CPPA_LOG_WARNING_IF(stmt,message) \
if (stmt) { CPPA_LOG_WARNING(message); }; CPPA_VOID_STMT
#define CPPA_LOG_INFO_IF(stmt,message) \
if (stmt) { CPPA_LOG_INFO(message); }; CPPA_VOID_STMT
#define CPPA_LOG_DEBUG_IF(stmt,message) \
if (stmt) { CPPA_LOG_DEBUG(message); }; CPPA_VOID_STMT
#define CPPA_LOGF_ERROR_IF(stmt,message) \
if (stmt) { CPPA_LOGF_ERROR(message); }; CPPA_VOID_STMT
#define CPPA_LOGF_WARNING_IF(stmt,message) \
if (stmt) { CPPA_LOGF_WARNING(message); }; CPPA_VOID_STMT
/**
* @brief Logs @p msg with custom member function @p fname.
*/
#define CPPA_LOGS_ERROR(fname, msg) CPPA_LOGC_ERROR(CPPA_CLASS_NAME, fname, msg)
#define CPPA_LOGF_INFO_IF(stmt,message) \
if (stmt) { CPPA_LOGF_INFO(message); }; CPPA_VOID_STMT
/**
* @brief Logs @p msg with custom class name @p cname.
*/
#define CPPA_LOGM_ERROR(cname, msg) CPPA_LOGC_ERROR(cname, __FUNCTION__, msg)
#define CPPA_LOGF_DEBUG_IF(stmt,message) \
if (stmt) { CPPA_LOGF_DEBUG(message); }; CPPA_VOID_STMT
/**
* @brief Logs @p msg in a free function if @p stmt evaluates to @p true.
*/
#define CPPA_LOGF_ERROR_IF(stmt, msg) CPPA_LIF((stmt), CPPA_LOGF_ERROR(msg))
/**
* @brief Logs @p msg in a free function.
*/
#define CPPA_LOGF_ERROR(msg) CPPA_LOGM_ERROR("NONE", msg)
#endif // CPPA_DEBUG
/**
* @brief Logs @p msg in a member function if @p stmt evaluates to @p true.
*/
#define CPPA_LOG_ERROR_IF(stmt, msg) CPPA_LIF((stmt), CPPA_LOG_ERROR(msg))
/**
* @brief Logs @p msg in a member function.
*/
#define CPPA_LOG_ERROR(msg) CPPA_LOGM_ERROR(CPPA_CLASS_NAME, msg)
// convenience macros for warnings
#define CPPA_LOG_WARNING(msg) CPPA_LOGM_WARNING(CPPA_CLASS_NAME, msg)
#define CPPA_LOG_WARNING_IF(stmt,msg) CPPA_LIF((stmt), CPPA_LOG_WARNING(msg))
#define CPPA_LOGF_WARNING(msg) CPPA_LOGM_WARNING("NONE", msg)
#define CPPA_LOGF_WARNING_IF(stmt,msg) CPPA_LIF((stmt), CPPA_LOGF_WARNING(msg))
#define CPPA_LOGM_WARNING(cname, msg) CPPA_LOGC_WARNING(cname, __FUNCTION__, msg)
// convenience macros for info messages
#define CPPA_LOG_INFO(msg) CPPA_LOGM_INFO(CPPA_CLASS_NAME, msg)
#define CPPA_LOG_INFO_IF(stmt,msg) CPPA_LIF((stmt), CPPA_LOG_INFO(msg))
#define CPPA_LOGF_INFO(msg) CPPA_LOGM_INFO("NONE", msg)
#define CPPA_LOGF_INFO_IF(stmt,msg) CPPA_LIF((stmt), CPPA_LOGF_INFO(msg))
#define CPPA_LOGM_INFO(cname, msg) CPPA_LOGC_INFO(cname, __FUNCTION__, msg)
// convenience macros for debug messages
#define CPPA_LOG_DEBUG(msg) CPPA_LOGM_DEBUG(CPPA_CLASS_NAME, msg)
#define CPPA_LOG_DEBUG_IF(stmt,msg) CPPA_LIF((stmt), CPPA_LOG_DEBUG(msg))
#define CPPA_LOGF_DEBUG(msg) CPPA_LOGM_DEBUG("NONE", msg)
#define CPPA_LOGF_DEBUG_IF(stmt,msg) CPPA_LIF((stmt), CPPA_LOGF_DEBUG(msg))
#define CPPA_LOGM_DEBUG(cname, msg) CPPA_LOGC_DEBUG(cname, __FUNCTION__, msg)
// convenience macros for trace messages
#define CPPA_LOGS_TRACE(fname, msg) CPPA_LOGC_TRACE(CPPA_CLASS_NAME, fname, msg)
#define CPPA_LOGM_TRACE(cname, msg) CPPA_LOGC_TRACE(cname, __FUNCTION__, msg)
#define CPPA_LOGF_TRACE(msg) CPPA_LOGM_TRACE("NONE", msg)
#define CPPA_LOG_TRACE(msg) CPPA_LOGM_TRACE(CPPA_CLASS_NAME, msg)
// convenience macros to safe some typing when printing arguments
#define CPPA_ARG(arg) #arg << " = " << arg
#define CPPA_TARG(arg, trans) #arg << " = " << trans ( arg )
#define CPPA_MARG(arg, memfun) #arg << " = " << arg . memfun ()
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 3 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_MAILBOX_BASED_HPP
#define CPPA_MAILBOX_BASED_HPP
#include <type_traits>
#include "cppa/detail/sync_request_bouncer.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
#include "cppa/intrusive/blocking_single_reader_queue.hpp"
namespace cppa {
template<typename T>
struct has_blocking_receive;
template<class Base, class Subtype>
class mailbox_based : public Base {
typedef detail::disposer del;
public:
~mailbox_based() {
if (!m_mailbox.closed()) {
detail::sync_request_bouncer f{this->exit_reason()};
m_mailbox.close(f);
}
}
protected:
typedef mailbox_based combined_type;
typedef detail::recursive_queue_node mailbox_element;
typedef typename std::conditional<
has_blocking_receive<Subtype>::value,
intrusive::blocking_single_reader_queue<mailbox_element,del>,
intrusive::single_reader_queue<mailbox_element,del>
>::type
mailbox_type;
template<typename... Ts>
mailbox_based(Ts&&... args) : Base(std::forward<Ts>(args)...) { }
void cleanup(std::uint32_t reason) {
detail::sync_request_bouncer f{reason};
m_mailbox.close(f);
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;
private:
inline Subtype* dthis() { return static_cast<Subtype*>(this); }
};
} // namespace cppa
#endif //CPPA_MAILBOX_BASED_HPP
......@@ -41,6 +41,7 @@
#include "cppa/util/rm_option.hpp"
#include "cppa/util/purge_refs.hpp"
#include "cppa/util/apply_args.hpp"
#include "cppa/util/disjunction.hpp"
#include "cppa/util/left_or_right.hpp"
#include "cppa/util/deduce_ref_type.hpp"
......@@ -86,7 +87,7 @@ struct invoke_policy_impl : invoke_policy_base<FilteredPattern> {
template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& type_token,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple& tup) {
typedef typename match_impl_from_type_list<
......@@ -118,7 +119,7 @@ struct invoke_policy_impl<wildcard_position::nil,
template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type&,
const std::type_info& type_token,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple& tup) {
return can_invoke(type_token, tup);
......@@ -161,7 +162,7 @@ struct invoke_policy_impl<wildcard_position::nil,
template<typename PtrType, class Tuple>
static bool prepare_invoke(tuple_type& result,
const std::type_info&,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple& tup,
typename std::enable_if<
......@@ -185,7 +186,7 @@ struct invoke_policy_impl<wildcard_position::nil,
template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types,
detail::tuple_impl_info timpl,
bool dynamically_typed,
PtrType* native_arg,
Tuple& tup,
typename std::enable_if<
......@@ -210,7 +211,7 @@ struct invoke_policy_impl<wildcard_position::nil,
}
// 'fall through'
}
else if (timpl == detail::dynamically_typed) {
else if (dynamically_typed) {
auto& arr = arr_type::arr;
if (tup.size() != sizeof...(Ts)) {
return false;
......@@ -252,7 +253,7 @@ struct invoke_policy_impl<wildcard_position::leading,
template<typename PtrType, typename Tuple>
static inline bool prepare_invoke(typename super::tuple_type&,
const std::type_info&,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple&) {
return true;
......@@ -290,7 +291,7 @@ struct invoke_policy_impl<wildcard_position::trailing,
template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple& tup) {
if (!can_invoke(arg_types, tup)) return false;
......@@ -330,7 +331,7 @@ struct invoke_policy_impl<wildcard_position::leading,
template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple& tup) {
if (!can_invoke(arg_types, tup)) return false;
......@@ -486,7 +487,7 @@ inline bool unroll_expr(PPFPs&,
std::uint64_t,
minus1l,
const std::type_info&,
detail::tuple_impl_info,
bool,
PtrType*,
Tuple&) {
return false;
......@@ -498,11 +499,11 @@ bool unroll_expr(PPFPs& fs,
std::uint64_t bitmask,
long_constant<N>,
const std::type_info& type_token,
detail::tuple_impl_info iinfo,
bool is_dynamic,
PtrType* ptr,
Tuple& tup) {
if (unroll_expr(fs, invoke_res, bitmask, long_constant<N-1>{},
type_token, iinfo, ptr, tup)) {
type_token, is_dynamic, ptr, tup)) {
return true;
}
if ((bitmask & (0x01 << N)) == 0) return false;
......@@ -511,7 +512,7 @@ bool unroll_expr(PPFPs& fs,
typedef typename Fun::pattern_type pattern_type;
typedef detail::invoke_policy<pattern_type> policy;
typename policy::tuple_type targs;
if (policy::prepare_invoke(targs, type_token, iinfo, ptr, tup)) {
if (policy::prepare_invoke(targs, type_token, is_dynamic, ptr, tup)) {
auto is = util::get_indices(targs);
util::void_type dummy;
typename has_bool_result<typename Fun::second_type>::token_type stoken;
......@@ -626,6 +627,8 @@ class match_expr {
public:
static constexpr bool may_have_timeout = false;
typedef util::type_list<Cases...> cases_list;
static constexpr size_t num_cases = sizeof...(Cases);
......@@ -666,7 +669,7 @@ class match_expr {
bool can_invoke(const any_tuple& tup) {
auto type_token = tup.type_token();
if (tup.impl_type() == detail::statically_typed) {
if (not tup.dynamically_typed()) {
auto bitmask = get_cache_entry(type_token, tup);
return bitmask != 0;
}
......@@ -713,7 +716,6 @@ class match_expr {
>::type
ptr_type;
auto impl_type = detail::statically_typed;
ptr_type ptr_arg = nullptr;
bool invoke_result = true;
bool unroll_result = unroll_expr(m_cases,
......@@ -721,7 +723,7 @@ class match_expr {
bitmask,
idx_token,
type_token,
impl_type,
false, // not dynamically_typed
ptr_arg,
static_cast<ref_type>(tup));
return unroll_result && invoke_result;
......@@ -797,7 +799,7 @@ class match_expr {
std::uint64_t get_cache_entry(const std::type_info* type_token,
const Tuple& value) {
CPPA_REQUIRE(type_token != nullptr);
if (value.impl_type() == detail::dynamically_typed) {
if (value.dynamically_typed()) {
return m_dummy.second; // all groups enabled
}
size_t i = find_token_pos(type_token);
......@@ -831,14 +833,14 @@ class match_expr {
auto ndp = fetch_native_data(vals, mutator_token);
auto token_ptr = vals->type_token();
auto bitmask = get_cache_entry(token_ptr, *vals);
auto impl_type = vals->impl_type();
auto dynamically_typed = vals->dynamically_typed();
bool invoke_result = true; // maybe set to false by an invoked functor
bool unroll_result = unroll_expr(m_cases,
invoke_result,
bitmask,
idx_token,
*token_ptr,
impl_type,
dynamically_typed,
ndp,
*vals);
return invoke_result && unroll_result;
......@@ -889,95 +891,95 @@ match_expr_collect(const Arg0& arg0, const Args&... args) {
return {all_cases};
}
template<bool HasTimeout>
struct match_expr_concat_impl {
template<typename Arg0, typename... Args>
static detail::behavior_impl* _(const Arg0& arg0, const Args&... args) {
typename detail::tdata_from_type_list<
namespace detail {
typedef std::true_type with_timeout;
typedef std::false_type without_timeout;
// with timeout
// end of recursion
template<class Data, class Token, typename F>
behavior_impl* concat_rec(const Data& data, Token, const timeout_definition<F>& arg) {
typedef typename match_expr_from_type_list<Token>::type combined_type;
return new default_behavior_impl<combined_type,F>{data, arg};
}
// recursive concatenation function
template<class Data, class Token, typename T, typename... Ts>
behavior_impl* concat_rec(const Data& data, Token, const T& arg, const Ts&... args) {
typedef typename util::tl_concat<
Token,
typename T::cases_list
>::type
next_token_type;
typename tdata_from_type_list<
typename util::tl_map<
typename util::tl_concat<
typename Arg0::cases_list,
typename Args::cases_list...
>::type,
next_token_type,
gref_wrapped
>::type
>::type
all_cases;
typedef typename match_expr_from_type_list<
typename util::tl_concat<
typename Arg0::cases_list,
typename Args::cases_list...
>::type
>::type
combined_type;
auto lvoid = []() { };
typedef detail::default_behavior_impl<combined_type,decltype(lvoid)>
impl_type;
detail::collect_tdata(all_cases, arg0.cases(), args.cases()...);
return new impl_type(all_cases, util::duration{}, lvoid);
}
};
template<>
struct match_expr_concat_impl<true> {
next_data;
next_token_type next_token;
collect_tdata(next_data, data, arg.cases());
return concat_rec(next_data, next_token, args...);
}
template<class TData, class Token, typename F>
static detail::behavior_impl* __(const TData& data, Token, const timeout_definition<F>& arg0) {
typedef typename match_expr_from_type_list<Token>::type combined_type;
typedef detail::default_behavior_impl<combined_type,F> impl_type;
return new impl_type(data, arg0);
}
template<typename F>
behavior_impl* concat_expr(with_timeout, const timeout_definition<F>& arg) {
typedef default_behavior_impl<dummy_match_expr,F> impl_type;
return new impl_type(dummy_match_expr{}, arg);
}
template<class TData, class Token, typename... Cases, typename... Args>
static detail::behavior_impl* __(const TData& data, Token, const match_expr<Cases...>& arg0, const Args&... args) {
typedef typename util::tl_concat<
Token,
util::type_list<Cases...>
>::type
next_token_type;
typename detail::tdata_from_type_list<
typename util::tl_map<
next_token_type,
gref_wrapped
>::type
template<typename T, typename... Ts>
behavior_impl* concat_expr(with_timeout, const T& arg, const Ts&... args) {
typename tdata_from_type_list<
typename util::tl_map<
typename T::cases_list,
gref_wrapped
>::type
next_data;
next_token_type next_token;
detail::collect_tdata(next_data, data, arg0.cases());
return __(next_data, next_token, args...);
}
>::type
wrapper;
detail::collect_tdata(wrapper, arg.cases());
return concat_rec(wrapper, typename T::cases_list{}, args...);
}
template<typename F>
static detail::behavior_impl* _(const timeout_definition<F>& arg0) {
typedef detail::default_behavior_impl<detail::dummy_match_expr,F> impl_type;
return new impl_type(detail::dummy_match_expr{}, arg0);
}
// without timeout
template<typename... Cases, typename... Args>
static detail::behavior_impl* _(const match_expr<Cases...>& arg0, const Args&... args) {
util::type_list<Cases...> token;
typename detail::tdata_from_type_list<
typename util::tl_map<
util::type_list<Cases...>,
gref_wrapped
template<typename T, typename... Ts>
behavior_impl* concat_expr(without_timeout, const T& arg, const Ts&... args) {
typename tdata_from_type_list<
typename util::tl_map<
typename util::tl_concat<
typename T::cases_list,
typename Ts::cases_list...
>::type,
gref_wrapped
>::type
>::type
all_cases;
typedef typename match_expr_from_type_list<
typename util::tl_concat<
typename T::cases_list,
typename Ts::cases_list...
>::type
>::type
wrapper;
detail::collect_tdata(wrapper, arg0.cases());
return __(wrapper, token, args...);
}
};
combined_type;
auto lvoid = []() { };
typedef default_behavior_impl<combined_type,decltype(lvoid)> impl_type;
collect_tdata(all_cases, arg.cases(), args.cases()...);
return new impl_type(all_cases, util::duration{}, lvoid);
}
template<typename Arg0, typename... Args>
intrusive_ptr<detail::behavior_impl> match_expr_concat(const Arg0& arg0,
const Args&... args) {
constexpr bool has_timeout = util::disjunction<
is_timeout_definition<Arg0>,
is_timeout_definition<Args>...>::value;
return {match_expr_concat_impl<has_timeout>::_(arg0, args...)};
template<typename T, typename... Ts>
behavior_impl_ptr match_expr_concat(const T& arg, const Ts&... args) {
std::integral_constant<bool,util::disjunction<T::may_have_timeout,Ts::may_have_timeout...>::value> token;
// use static call dispatch to select correct function
return concat_expr(token, arg, args...);
}
} // namespace detail
} // namespace cppa
#endif // CPPA_MATCH_EXPR_HPP
......@@ -28,8 +28,8 @@
\******************************************************************************/
#ifndef MEMORY_CACHED_MIXIN_HPP
#define MEMORY_CACHED_MIXIN_HPP
#ifndef CPPA_MEMORY_CACHED_HPP
#define CPPA_MEMORY_CACHED_HPP
#include "cppa/detail/memory.hpp"
......@@ -39,19 +39,21 @@ namespace cppa {
* @brief This mixin adds all member functions and member variables needed
* by the memory management subsystem.
*/
template<typename Base>
class memory_cached_mixin : public Base {
template<class Base, class Subtype>
class memory_cached : public Base {
friend class detail::memory;
template<typename T>
template<typename>
friend class detail::basic_memory_cache;
protected:
template<typename... Args>
memory_cached_mixin(Args&&... args)
: Base(std::forward<Args>(args)...), outer_memory(nullptr) { }
typedef memory_cached combined_type;
template<typename... Ts>
memory_cached(Ts&&... args) : Base(std::forward<Ts>(args)...)
, outer_memory(nullptr) { }
virtual void request_deletion() {
auto mc = detail::memory::get_cache_map_entry(&typeid(*this));
......@@ -74,4 +76,4 @@ class memory_cached_mixin : public Base {
} // namespace cppa
#endif // MEMORY_CACHED_MIXIN_HPP
#endif // CPPA_MEMORY_CACHED_HPP
......@@ -31,9 +31,11 @@
#ifndef MESSAGE_FUTURE_HPP
#define MESSAGE_FUTURE_HPP
#include <cstdint>
#include <type_traits>
#include "cppa/on.hpp"
#include "cppa/atom.hpp"
#include "cppa/behavior.hpp"
#include "cppa/match_expr.hpp"
#include "cppa/message_id.hpp"
......@@ -130,12 +132,8 @@ class message_future {
message_future(const message_future&) = default;
message_future& operator=(const message_future&) = default;
# ifndef CPPA_DOCUMENTATION
inline message_future(const message_id_t& from) : m_mid(from) { }
# endif
private:
message_id_t m_mid;
......@@ -147,7 +145,7 @@ class message_future {
return false;
};
return {
on<atom("EXITED"),std::uint32_t>() >> skip_message,
on(atom("EXITED"), val<std::uint32_t>) >> skip_message,
on(atom("VOID")) >> skip_message,
on(atom("TIMEOUT")) >> handle_sync_timeout,
(on(any_vals, arg_match) >> fs)...
......
......@@ -31,12 +31,11 @@
#ifndef DEFAULT_ACTOR_PROXY_HPP
#define DEFAULT_ACTOR_PROXY_HPP
#include "cppa/extend.hpp"
#include "cppa/actor_proxy.hpp"
#include "cppa/memory_cached_mixin.hpp"
#include "cppa/memory_cached.hpp"
#include "cppa/network/default_protocol.hpp"
#include "cppa/detail/abstract_actor.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa { namespace detail {
......@@ -49,7 +48,8 @@ class basic_memory_cache;
namespace cppa { namespace network {
class sync_request_info : public memory_cached_mixin<memory_managed> {
class sync_request_info : public extend<memory_managed,sync_request_info>::
with<memory_cached> {
friend class detail::memory;
......@@ -67,9 +67,9 @@ class sync_request_info : public memory_cached_mixin<memory_managed> {
};
class default_actor_proxy : public detail::abstract_actor<actor_proxy> {
class default_actor_proxy : public actor_proxy {
typedef detail::abstract_actor<actor_proxy> super;
typedef actor_proxy super;
public:
......@@ -77,19 +77,19 @@ class default_actor_proxy : public detail::abstract_actor<actor_proxy> {
const process_information_ptr& pinfo,
const default_protocol_ptr& parent);
void enqueue(actor* sender, any_tuple msg);
void enqueue(const actor_ptr& sender, any_tuple msg);
void sync_enqueue(actor* sender, message_id_t id, any_tuple msg);
void sync_enqueue(const actor_ptr& sender, message_id_t id, any_tuple msg);
void link_to(const intrusive_ptr<actor>& other);
void link_to(const actor_ptr& other);
void unlink_from(const intrusive_ptr<actor>& other);
void unlink_from(const actor_ptr& other);
bool remove_backlink(const intrusive_ptr<actor>& to);
bool remove_backlink(const actor_ptr& to);
bool establish_backlink(const intrusive_ptr<actor>& to);
bool establish_backlink(const actor_ptr& to);
void local_link_to(const intrusive_ptr<actor>& other);
void local_link_to(const actor_ptr& other);
void local_unlink_from(const actor_ptr& other);
......
......@@ -223,6 +223,11 @@ struct pattern_type : pattern_type_<util::is_callable<T>::value && !detail::is_b
namespace cppa {
/**
* @brief A wildcard that matches any number of any values.
*/
constexpr anything any_vals = anything{};
#ifdef CPPA_DOCUMENTATION
/**
......@@ -251,12 +256,6 @@ constexpr __unspecified__ skip_message;
template<typename T>
__unspecified__ val();
/**
* @brief A wildcard that matches any number of any values.
*/
constexpr anything any_vals;
/**
* @brief Left-hand side of a partial function expression that matches values.
*
......@@ -294,8 +293,6 @@ constexpr typename detail::boxed<T>::type val() {
return typename detail::boxed<T>::type();
}
constexpr anything any_vals = anything();
typedef typename detail::boxed<util::arg_match_t>::type boxed_arg_match_t;
constexpr boxed_arg_match_t arg_match = boxed_arg_match_t();
......@@ -414,7 +411,7 @@ class on_the_fly_rvalue_builder {
constexpr detail::on_the_fly_rvalue_builder on_arg_match;
#endif
#endif // CPPA_DOCUMENTATION
} // namespace cppa
......
......@@ -35,6 +35,7 @@
#include <vector>
#include <memory>
#include <utility>
#include <type_traits>
#include "cppa/behavior.hpp"
#include "cppa/any_tuple.hpp"
......@@ -62,6 +63,8 @@ class partial_function {
typedef intrusive_ptr<detail::behavior_impl> impl_ptr;
static constexpr bool may_have_timeout = false;
partial_function() = default;
partial_function(partial_function&&) = default;
partial_function(const partial_function&) = default;
......@@ -95,28 +98,23 @@ class partial_function {
return behavior{m_impl->or_else(other.m_impl)};
}
template<typename F>
inline behavior or_else(const timeout_definition<F>& tdef) const {
generic_timeout_definition gtd{tdef.timeout, tdef.handler};
return behavior{m_impl->copy(gtd)};
}
template<typename... Cases>
inline partial_function or_else(const match_expr<Cases...>& mexpr) const {
return m_impl->or_else(mexpr.as_behavior_impl());
}
template<typename Arg0, typename Arg1, typename... Args>
typename util::if_else<
util::disjunction<
is_timeout_definition<Arg0>,
is_timeout_definition<Arg1>,
is_timeout_definition<Args>...>,
behavior,
util::wrapped<partial_function> >::type
or_else(const Arg0& arg0, const Arg1& arg1, const Args&... args) const {
return m_impl->or_else(match_expr_concat(arg0, arg1, args...));
}
template<typename T>
typename std::conditional<T::may_have_timeout,behavior,partial_function>::type
or_else(const T& arg) const;
template<typename T0, typename T1, typename... Ts>
typename std::conditional<
util::disjunction<
T0::may_have_timeout,
T1::may_have_timeout,
Ts::may_have_timeout...
>::value,
behavior,
partial_function
>::type
or_else(const T0& arg0, const T1& arg1, const Ts&... args) const;
inline const impl_ptr& as_behavior_impl() const;
private:
......@@ -124,15 +122,45 @@ class partial_function {
};
template<typename Arg0, typename... Args>
typename util::if_else<
util::disjunction<
is_timeout_definition<Arg0>,
is_timeout_definition<Args>...>,
behavior,
util::wrapped<partial_function> >::type
match_expr_convert(const Arg0& arg0, const Args&... args) {
return {match_expr_concat(arg0, args...)};
template<typename T, typename... Ts>
typename std::conditional<
util::disjunction<
T::may_have_timeout,
Ts::may_have_timeout...
>::value,
behavior,
partial_function
>::type
match_expr_convert(const T& arg0, const Ts&... args) {
return detail::match_expr_concat(arg0, args...);
}
/******************************************************************************
* inline and template member function implementations *
******************************************************************************/
inline const partial_function::impl_ptr& partial_function::as_behavior_impl() const {
return m_impl;
}
template<typename T>
typename std::conditional<T::may_have_timeout,behavior,partial_function>::type
partial_function::or_else(const T& arg) const {
return m_impl->or_else(arg.as_behavior_impl());
}
template<typename T0, typename T1, typename... Ts>
typename std::conditional<
util::disjunction<
T0::may_have_timeout,
T1::may_have_timeout,
Ts::may_have_timeout...
>::value,
behavior,
partial_function
>::type
partial_function::or_else(const T0& arg0, const T1& arg1, const Ts&... args) const {
return m_impl->or_else(match_expr_convert(arg0, arg1, args...).as_behavior_impl());
}
} // namespace cppa
......
......@@ -293,8 +293,6 @@ T& get_ref(primitive_variant& pv) {
return pv.get_as<ptype>();
}
#ifdef CPPA_DOCUMENTATION
/**
* @ingroup TypeSystem
* @brief Casts a primitive variant to its C++ type.
......@@ -304,7 +302,11 @@ T& get_ref(primitive_variant& pv) {
* @returns A const reference to the value of @p pv of type @p T.
*/
template<primitive_type PT>
const T& get_ref(const primitive_variant& pv);
inline const typename detail::ptype_to_type<PT>::type&
get(const primitive_variant& pv) {
static_assert(PT != pt_null, "PT == pt_null");
return get<typename detail::ptype_to_type<PT>::type>(pv);
}
/**
* @ingroup TypeSystem
......@@ -314,18 +316,6 @@ const T& get_ref(const primitive_variant& pv);
* @param pv A primitive variant of type @p T.
* @returns A reference to the value of @p pv of type @p T.
*/
template<primitive_type PT>
T& get_ref(primitive_variant& pv);
#else
template<primitive_type PT>
inline const typename detail::ptype_to_type<PT>::type&
get(const primitive_variant& pv) {
static_assert(PT != pt_null, "PT == pt_null");
return get<typename detail::ptype_to_type<PT>::type>(pv);
}
template<primitive_type PT>
inline typename detail::ptype_to_type<PT>::type&
get_ref(primitive_variant& pv) {
......@@ -333,8 +323,6 @@ get_ref(primitive_variant& pv) {
return get_ref<typename detail::ptype_to_type<PT>::type>(pv);
}
#endif
/**
* @relates primitive_variant
*/
......
......@@ -50,6 +50,10 @@ 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");
protected:
typedef sb_actor combined_type;
public:
/**
......
......@@ -34,7 +34,14 @@
#include <functional>
#include "cppa/config.hpp"
#include "cppa/extend.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
namespace cppa {
......@@ -47,48 +54,167 @@ enum class resume_result {
};
enum scheduled_actor_type {
context_switching_impl,
event_based_impl,
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;
template<>
struct has_blocking_receive<scheduled_actor> : std::true_type { };
/**
* @brief A base class for cooperatively scheduled actors.
* @extends local_actor
*/
class scheduled_actor : public local_actor {
class scheduled_actor : public extend<local_actor,scheduled_actor>::with<mailbox_based>{
typedef combined_type super;
public:
scheduled_actor(bool enable_chained_send = false);
static constexpr bool has_blocking_receive = false;
~scheduled_actor();
/**
* @brief Intrusive next pointer needed by the scheduler's job queue.
*/
scheduled_actor* next;
// called from worker thread,
// actors sets next_job to its chained actor
/**
* @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, actor_ptr& next_job) = 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;
inline bool is_hidden() const { return m_hidden; }
/**
* @brief Returns @p true if this actor is ignored by
* {@link await_all_others_done()}, false otherwise.
*/
inline bool is_hidden() const;
void enqueue(const actor_ptr&, any_tuple);
bool chained_enqueue(const actor_ptr&, any_tuple);
void sync_enqueue(const actor_ptr&, message_id_t, any_tuple);
bool chained_sync_enqueue(const actor_ptr&, message_id_t, any_tuple);
void request_timeout(const util::duration& d);
inline bool has_pending_timeout() const {
return m_has_pending_tout;
}
inline void reset_timeout() {
if (m_has_pending_tout) {
++m_pending_tout;
m_has_pending_tout = false;
}
}
inline void handle_timeout(behavior& bhvr) {
bhvr.handle_timeout();
reset_timeout();
}
inline void push_timeout() {
++m_pending_tout;
}
inline void pop_timeout() {
CPPA_REQUIRE(m_pending_tout > 0);
--m_pending_tout;
}
inline bool waits_for_timeout(std::uint32_t timeout_id) {
return m_has_pending_tout && m_pending_tout == timeout_id;
}
protected:
scheduled_actor(actor_state init_state, bool enable_chained_send);
void cleanup(std::uint32_t reason);
typedef detail::recursive_queue_node mailbox_element;
typedef intrusive::single_reader_queue<mailbox_element,detail::disposer>
mailbox_type;
actor_state compare_exchange_state(actor_state expected, actor_state desired);
template<typename... Ts>
inline mailbox_element* new_mailbox_element(Ts&&... args) {
return mailbox_element::create(std::forward<Ts>(args)...);
}
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(actor_state next_state, bool* failed, mailbox_element* e);
template<typename... Ts>
inline bool enqueue_impl(actor_state next, Ts&&... args) {
auto ptr = this->new_mailbox_element(std::forward<Ts>(args)...);
return enqueue(next, nullptr, ptr);
}
bool sync_enqueue_impl(actor_state next,
const actor_ptr& sender,
any_tuple& msg,
message_id_t id);
bool m_has_pending_tout;
std::uint32_t m_pending_tout;
std::atomic<actor_state> m_state;
protected:
scheduler* m_scheduler;
bool m_hidden;
bool initialized();
mailbox_type m_mailbox;
};
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
......@@ -79,7 +79,7 @@ class scheduler {
scheduler_helper* m_helper;
channel* delayed_send_helper();
const actor_ptr& delayed_send_helper();
friend class detail::singleton_manager;
......@@ -173,18 +173,13 @@ class scheduler {
init_callback init_cb,
void_function actor_behavior) = 0;
// hide implementation details for documentation
# ifndef CPPA_DOCUMENTATION
template<typename F, typename T0, typename... Ts>
template<typename F, typename T, typename... Ts>
actor_ptr exec(spawn_options opts, init_callback cb,
F f, T0&& a0, Ts&&... as) {
return this->exec(opts, cb, std::bind(f, detail::fwd<T0>(a0),
F f, T&& a0, Ts&&... as) {
return this->exec(opts, cb, std::bind(f, detail::fwd<T>(a0),
detail::fwd<Ts>(as)...));
}
# endif // CPPA_DOCUMENTATION
};
/**
......@@ -201,11 +196,6 @@ void set_scheduler(scheduler* sched);
*/
void set_default_scheduler(size_t num_threads);
/**
* @brief Returns the currently running scheduler.
*/
scheduler* get_scheduler();
} // namespace cppa
#endif // CPPA_SCHEDULER_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 3 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_SINGLETONS_HPP
#define CPPA_SINGLETONS_HPP
#include "cppa/detail/singleton_manager.hpp"
namespace cppa {
inline logging* get_logger() {
return detail::singleton_manager::get_logger();
}
inline scheduler* get_scheduler() {
return detail::singleton_manager::get_scheduler();
}
inline detail::group_manager* get_group_manager() {
return detail::singleton_manager::get_group_manager();
}
inline detail::actor_registry* get_actor_registry() {
return detail::singleton_manager::get_actor_registry();
}
inline network::middleman* get_middleman() {
return detail::singleton_manager::get_middleman();
}
inline detail::uniform_type_info_map* get_uniform_type_info_map() {
return detail::singleton_manager::get_uniform_type_info_map();
}
inline detail::abstract_tuple* get_tuple_dummy() {
return detail::singleton_manager::get_tuple_dummy();
}
inline detail::empty_tuple* get_empty_tuple() {
return detail::singleton_manager::get_empty_tuple();
}
inline detail::decorated_names_map* get_decorated_names_map() {
return detail::singleton_manager::get_decorated_names_map();
}
} // namespace cppa
#endif // CPPA_SINGLETONS_HPP
......@@ -41,6 +41,9 @@ namespace cppa {
/**
* @brief Stores options passed to the @p spawn function family.
*/
#ifdef CPPA_DOCUMENTATION
class spawn_options { };
#else
enum class spawn_options : int {
no_flags = 0x00,
link_flag = 0x01,
......@@ -49,6 +52,7 @@ enum class spawn_options : int {
hide_flag = 0x08,
blocking_api_flag = 0x10
};
#endif
#ifndef CPPA_DOCUMENTATION
namespace {
......
......@@ -31,24 +31,36 @@
#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/detail/behavior_stack.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
namespace cppa { namespace detail {
namespace cppa {
template<class Derived, class Base>
class stacked_actor_mixin : public Base {
/**
* @tparam Base Either @p scheduled or @p threaded.
*/
template<class Base, class Subtype>
class stacked : public Base {
friend class receive_policy;
friend class detail::receive_policy;
friend class detail::behavior_stack;
protected:
typedef stacked combined_type;
public:
static constexpr auto receive_flag = detail::rp_nestable;
virtual void dequeue(partial_function& fun) {
m_recv_policy.receive(dthis(), fun);
}
......@@ -62,19 +74,28 @@ class stacked_actor_mixin : public Base {
}
virtual void run() {
if (!this->m_bhvr_stack.empty()) {
this->m_bhvr_stack.exec(m_recv_policy, dthis());
if (!dthis()->m_bhvr_stack.empty()) {
dthis()->exec_behavior_stack();
}
if (m_behavior) {
m_behavior();
}
}
protected:
inline void set_behavior(std::function<void()> fun) {
m_behavior = std::move(fun);
}
virtual void quit(std::uint32_t reason) {
this->cleanup(reason);
throw actor_exited(reason);
}
stacked_actor_mixin() = default;
protected:
stacked_actor_mixin(std::function<void()> f) : m_behavior(std::move(f)) { }
template<typename... Args>
stacked(std::function<void()> fun, Args&&... args)
: Base(std::forward<Args>(args)...), m_behavior(std::move(fun)) { }
virtual void do_become(behavior&& bhvr, bool discard_old) {
become_impl(std::move(bhvr), discard_old, message_id_t());
......@@ -85,36 +106,42 @@ class stacked_actor_mixin : public Base {
}
virtual bool has_behavior() {
return static_cast<bool>(m_behavior)
|| !this->m_bhvr_stack.empty();
return static_cast<bool>(m_behavior) || !dthis()->m_bhvr_stack.empty();
}
typedef std::chrono::time_point<std::chrono::high_resolution_clock>
timeout_type;
virtual timeout_type init_timeout(const util::duration& rel_time) = 0;
virtual detail::recursive_queue_node* await_message() = 0;
virtual detail::recursive_queue_node* await_message(const timeout_type& abs_time) = 0;
private:
std::function<void()> m_behavior;
receive_policy m_recv_policy;
inline Derived* dthis() {
return static_cast<Derived*>(this);
}
detail::receive_policy m_recv_policy;
void become_impl(behavior&& bhvr, bool discard_old, message_id_t mid) {
if (bhvr.timeout().valid()) {
dthis()->reset_timeout();
dthis()->request_timeout(bhvr.timeout());
}
if (!this->m_bhvr_stack.empty() && discard_old) {
this->m_bhvr_stack.pop_async_back();
if (!dthis()->m_bhvr_stack.empty() && discard_old) {
dthis()->m_bhvr_stack.pop_async_back();
}
this->m_bhvr_stack.push_back(std::move(bhvr), mid);
dthis()->m_bhvr_stack.push_back(std::move(bhvr), mid);
}
virtual void exec_behavior_stack() {
this->m_bhvr_stack.exec(m_recv_policy, dthis());
}
inline Subtype* dthis() { return static_cast<Subtype*>(this); }
};
} } // namespace cppa::detail
} // namespace cppa
#endif // CPPA_STACKED_ACTOR_MIXIN_HPP
......@@ -44,63 +44,39 @@
#include "cppa/atom.hpp"
#include "cppa/either.hpp"
#include "cppa/extend.hpp"
#include "cppa/stacked.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/intrusive_ptr.hpp"
#include "cppa/detail/abstract_actor.hpp"
#include "cppa/mailbox_based.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/stacked_actor_mixin.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/intrusive/blocking_single_reader_queue.hpp"
namespace cppa {
#ifdef CPPA_DOCUMENTATION
/**
* @brief An actor running in its own thread.
*/
class thread_mapped_actor : public local_actor {
protected:
/**
* @brief Implements the actor's behavior.
* Reimplemented this function for a class-based actor.
* Returning from this member function will end the
* execution of the actor.
*/
virtual void run();
};
#else // CPPA_DOCUMENTATION
class self_type;
class scheduler_helper;
class thread_mapped_actor;
namespace detail {
typedef intrusive::blocking_single_reader_queue<recursive_queue_node,disposer>
blocking_mailbox;
typedef stacked_actor_mixin<thread_mapped_actor,
abstract_actor<local_actor,blocking_mailbox> >
thread_mapped_actor_base;
template<>
struct has_blocking_receive<thread_mapped_actor> : std::true_type { };
} // namespace detail
class thread_mapped_actor : public detail::thread_mapped_actor_base {
friend class self_type; // needs access to cleanup()
/**
* @brief An actor running in its own thread.
* @extends local_actor
*/
class thread_mapped_actor : public extend<local_actor,thread_mapped_actor>::with<stacked,mailbox_based> {
friend class detail::behavior_stack; // needs access to receive_node()
friend class detail::receive_policy;
friend class self_type; // needs access to cleanup()
friend class scheduler_helper; // needs access to mailbox
friend class detail::receive_policy; // needs access to await_message(), etc.
friend class detail::behavior_stack; // needs same access as receive_policy
typedef detail::thread_mapped_actor_base super;
typedef combined_type super;
public:
......@@ -108,59 +84,48 @@ class thread_mapped_actor : public detail::thread_mapped_actor_base {
thread_mapped_actor(std::function<void()> fun);
void quit(std::uint32_t reason = exit_reason::normal); //override
inline void initialized(bool value) { m_initialized = value; }
void enqueue(actor* sender, any_tuple msg); //override
bool initialized() const;
void sync_enqueue(actor* sender, message_id_t id, any_tuple msg);
void enqueue(const actor_ptr& sender, any_tuple msg);
inline decltype(m_mailbox)& mailbox() { return m_mailbox; }
void sync_enqueue(const actor_ptr& sender, message_id_t id, any_tuple msg);
inline void initialized(bool value) { m_initialized = value; }
// required by stacked_actor_mixin
inline void reset_timeout() { }
inline void request_timeout(const util::duration&) { }
inline void handle_timeout(behavior& bhvr) { bhvr.handle_timeout(); }
inline void pop_timeout() { }
inline void push_timeout() { }
inline bool waits_for_timeout(std::uint32_t) { return false; }
protected:
bool initialized();
typedef detail::recursive_queue_node mailbox_element;
typedef intrusive::blocking_single_reader_queue<mailbox_element,detail::disposer>
mailbox_type;
void cleanup(std::uint32_t reason);
private:
timeout_type init_timeout(const util::duration& rel_time);
detail::recursive_queue_node* await_message();
detail::recursive_queue_node* await_message(const timeout_type& abs_time);
bool m_initialized;
// required by nestable_receive_policy
static const detail::receive_policy_flag receive_flag = detail::rp_nestable;
inline void push_timeout() { }
inline void pop_timeout() { }
inline bool waits_for_timeout(std::uint32_t) { return false; }
inline detail::recursive_queue_node* receive_node() {
return m_mailbox.pop();
}
inline auto init_timeout(const util::duration& tout)
-> decltype(std::chrono::high_resolution_clock::now()) {
auto result = std::chrono::high_resolution_clock::now();
result += tout;
return result;
}
inline detail::recursive_queue_node* try_receive_node() {
return m_mailbox.try_pop();
}
template<typename Timeout>
inline detail::recursive_queue_node* try_receive_node(const Timeout& tout) {
return m_mailbox.try_pop(tout);
}
inline void handle_timeout(behavior& bhvr) {
bhvr.handle_timeout();
}
protected:
mailbox_type m_mailbox;
};
typedef intrusive_ptr<thread_mapped_actor> thread_mapped_actor_ptr;
#endif // CPPA_DOCUMENTATION
} // namespace cppa
#endif // CPPA_THREAD_BASED_ACTOR_HPP
......@@ -37,18 +37,16 @@
namespace cppa {
namespace detail { class behavior_impl; }
template<typename F>
struct timeout_definition {
static constexpr bool may_have_timeout = true;
util::duration timeout;
F handler;
detail::behavior_impl* as_behavior_impl() const;
};
template<typename T>
struct is_timeout_definition : std::false_type { };
template<typename F>
struct is_timeout_definition<timeout_definition<F> > : std::true_type { };
typedef timeout_definition<std::function<void()> > generic_timeout_definition;
} // namespace cppa
......
......@@ -35,12 +35,12 @@
namespace cppa { namespace util {
template<typename... BooleanConstants>
template<bool... Values>
struct disjunction;
template<typename Head, typename... Tail>
template<bool Head, bool... Tail>
struct disjunction<Head, Tail...>
: std::integral_constant<bool, Head::value || disjunction<Tail...>::value> {
: std::integral_constant<bool, Head || disjunction<Tail...>::value> {
};
template<>
......
......@@ -53,7 +53,7 @@ namespace cppa { namespace util {
template<typename What, typename With, typename... IfStmt>
struct replace_type {
static constexpr bool do_replace = disjunction<IfStmt...>::value;
static constexpr bool do_replace = disjunction<IfStmt::value...>::value;
typedef typename detail::replace_type<do_replace, What, With>::type
type;
};
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 3 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/detail/abstract_scheduled_actor.hpp"
namespace cppa { namespace detail {
void abstract_scheduled_actor::request_timeout(const util::duration& d) {
if (d.valid()) {
if (d.is_zero()) {
// immediately enqueue timeout
auto node = super::fetch_node(this,
make_any_tuple(atom("SYNC_TOUT"),
++m_active_timeout_id));
this->m_mailbox.enqueue(node);
}
else {
get_scheduler()->delayed_send(
this, d,
make_any_tuple(
atom("SYNC_TOUT"), ++m_active_timeout_id));
}
m_has_pending_timeout_request = true;
}
else m_has_pending_timeout_request = false;
}
abstract_scheduled_actor::abstract_scheduled_actor(int state)
: super(true), m_state(state), m_has_pending_timeout_request(false)
, m_active_timeout_id(0) {
}
bool abstract_scheduled_actor::chained_enqueue(actor* sender, any_tuple msg) {
return enqueue_node(super::fetch_node(sender, std::move(msg)), pending);
}
bool abstract_scheduled_actor::chained_sync_enqueue(actor* sender, message_id_t id, any_tuple msg) {
bool failed = false;
bool result = enqueue_node(super::fetch_node(sender, std::move(msg), id), pending, &failed);
if (failed) {
sync_request_bouncer f{this, exit_reason()};
f(sender, id);
}
return result;
}
void abstract_scheduled_actor::quit(std::uint32_t reason) {
this->cleanup(reason);
throw actor_exited(reason);
}
void abstract_scheduled_actor::enqueue(actor* sender, any_tuple msg) {
enqueue_node(super::fetch_node(sender, std::move(msg)));
}
void abstract_scheduled_actor::sync_enqueue(actor* sender, message_id_t id, any_tuple msg) {
bool failed = false;
enqueue_node(super::fetch_node(sender, std::move(msg), id), ready, &failed);
if (failed) {
sync_request_bouncer f{this, exit_reason()};
f(sender, id);
}
}
int abstract_scheduled_actor::compare_exchange_state(int expected, int new_value) {
int e = expected;
do {
if (m_state.compare_exchange_weak(e, new_value)) {
return new_value;
}
}
while (e == expected);
return e;
}
bool abstract_scheduled_actor::enqueue_node(recursive_queue_node* node,
int next_state,
bool* failed) {
CPPA_REQUIRE(next_state == ready || next_state == pending);
CPPA_REQUIRE(node->marked == false);
switch (this->m_mailbox.enqueue(node)) {
case intrusive::first_enqueued: {
int state = m_state.load();
for (;;) {
switch (state) {
case blocked: {
if (m_state.compare_exchange_weak(state, next_state)) {
CPPA_REQUIRE(this->m_scheduler != nullptr);
if (next_state == ready) {
this->m_scheduler->enqueue(this);
}
return true;
}
break;
}
case about_to_block: {
if (m_state.compare_exchange_weak(state, ready)) {
return false;
}
break;
}
default: return false;
}
}
break;
}
case intrusive::queue_closed: {
if (failed) *failed = true;
break;
}
default: break;
}
return false;
}
} } // namespace cppa::detail
......@@ -32,6 +32,8 @@
namespace cppa { namespace detail {
abstract_tuple::abstract_tuple(bool is_dynamic) : m_is_dynamic(is_dynamic) { }
bool abstract_tuple::equals(const abstract_tuple &other) const {
return this == &other
|| ( size() == other.size()
......@@ -39,9 +41,7 @@ bool abstract_tuple::equals(const abstract_tuple &other) const {
}
abstract_tuple::abstract_tuple(const abstract_tuple& other)
: ref_counted()
, m_impl_type(other.m_impl_type) {
}
: m_is_dynamic(other.m_is_dynamic) { }
const std::type_info* abstract_tuple::type_token() const {
return &typeid(void);
......
......@@ -36,7 +36,10 @@
#include <stdexcept>
#include "cppa/actor.hpp"
#include "cppa/config.hpp"
#include "cppa/logging.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/singletons.hpp"
#include "cppa/util/shared_spinlock.hpp"
#include "cppa/util/shared_lock_guard.hpp"
......@@ -46,26 +49,161 @@
namespace cppa {
namespace {
inline detail::actor_registry& registry() {
return *(detail::singleton_manager::get_actor_registry());
}
namespace { typedef std::unique_lock<std::mutex> guard_type; }
} // namespace <anonymous>
// m_exit_reason is guaranteed to be set to 0, i.e., exit_reason::not_exited,
// by std::atomic<> constructor
actor::actor(actor_id aid) : m_id(aid), m_is_proxy(true) { }
actor::actor(actor_id aid)
: m_id(aid), m_is_proxy(true), m_exit_reason(exit_reason::not_exited) { }
actor::actor() : m_id(registry().next_id()), m_is_proxy(false) { }
actor::actor()
: m_id(get_actor_registry()->next_id()), m_is_proxy(false)
, m_exit_reason(exit_reason::not_exited) {
CPPA_LOG_INFO("spawned new actor with ID " << id()
<< ", class = " << CPPA_CLASS_NAME);
}
bool actor::chained_enqueue(actor* sender, any_tuple msg) {
bool actor::chained_enqueue(const actor_ptr& sender, any_tuple msg) {
enqueue(sender, std::move(msg));
return false;
}
bool actor::chained_sync_enqueue(actor* ptr, message_id_t id, any_tuple msg) {
bool actor::chained_sync_enqueue(const actor_ptr& ptr, message_id_t id, any_tuple msg) {
sync_enqueue(ptr, id, std::move(msg));
return false;
}
bool actor::link_to_impl(const actor_ptr& other) {
if (other && other != this) {
guard_type guard(m_mtx);
// send exit message if already exited
if (exited()) {
other->enqueue(this, make_any_tuple(atom("EXIT"), exit_reason()));
}
// add link if not already linked to other
// (checked by establish_backlink)
else if (other->establish_backlink(this)) {
m_links.push_back(other);
return true;
}
}
return false;
}
bool actor::attach(attachable_ptr ptr) {
if (ptr == nullptr) {
guard_type guard(m_mtx);
return m_exit_reason == exit_reason::not_exited;
}
std::uint32_t reason;
{ // lifetime scope of guard
guard_type guard(m_mtx);
reason = m_exit_reason;
if (reason == exit_reason::not_exited) {
m_attachables.push_back(std::move(ptr));
return true;
}
}
ptr->actor_exited(reason);
return false;
}
void actor::detach(const attachable::token& what) {
attachable_ptr ptr;
{ // lifetime scope of guard
guard_type guard(m_mtx);
auto end = m_attachables.end();
auto i = std::find_if(
m_attachables.begin(), end,
[&](attachable_ptr& p) { return p->matches(what); });
if (i != end) {
ptr = std::move(*i);
m_attachables.erase(i);
}
}
// ptr will be destroyed here, without locked mutex
}
void actor::link_to(const actor_ptr& other) {
static_cast<void>(link_to_impl(other));
}
void actor::unlink_from(const actor_ptr& other) {
static_cast<void>(unlink_from_impl(other));
}
bool actor::remove_backlink(const actor_ptr& other) {
if (other && other != this) {
guard_type guard(m_mtx);
auto i = std::find(m_links.begin(), m_links.end(), other);
if (i != m_links.end()) {
m_links.erase(i);
return true;
}
}
return false;
}
bool actor::establish_backlink(const actor_ptr& other) {
std::uint32_t reason = exit_reason::not_exited;
if (other && other != this) {
guard_type guard(m_mtx);
reason = m_exit_reason;
if (reason == exit_reason::not_exited) {
auto i = std::find(m_links.begin(), m_links.end(), other);
if (i == m_links.end()) {
m_links.push_back(other);
return true;
}
}
}
// send exit message without lock
if (reason != exit_reason::not_exited) {
other->enqueue(this, make_any_tuple(atom("EXIT"), reason));
}
return false;
}
bool actor::unlink_from_impl(const actor_ptr& other) {
guard_type guard(m_mtx);
// remove_backlink returns true if this actor is linked to other
if (other && !exited() && other->remove_backlink(this)) {
auto i = std::find(m_links.begin(), m_links.end(), other);
CPPA_REQUIRE(i != m_links.end());
m_links.erase(i);
return true;
}
return false;
}
void actor::cleanup(std::uint32_t reason) {
CPPA_REQUIRE(reason != exit_reason::not_exited);
// move everyhting out of the critical section before processing it
decltype(m_links) mlinks;
decltype(m_attachables) mattachables;
{ // lifetime scope of guard
guard_type guard(m_mtx);
if (m_exit_reason != exit_reason::not_exited) {
// already exited
return;
}
m_exit_reason = reason;
mlinks = std::move(m_links);
mattachables = std::move(m_attachables);
// make sure lists are empty
m_links.clear();
m_attachables.clear();
}
// send exit messages
for (actor_ptr& aptr : mlinks) {
aptr->enqueue(this, make_any_tuple(atom("EXIT"), reason));
}
for (attachable_ptr& ptr : mattachables) {
ptr->actor_exited(reason);
}
}
} // namespace cppa
......@@ -76,7 +76,7 @@ void actor_registry::put(actor_id key, const actor_ptr& value) {
}
}
if (add_attachable) {
CPPA_LOG_INFO("added " << key);
CPPA_LOG_INFO("added actor with ID " << key);
struct eraser : attachable {
actor_id m_id;
actor_registry* m_registry;
......@@ -88,7 +88,7 @@ void actor_registry::put(actor_id key, const actor_ptr& value) {
return false;
}
};
value->attach(new eraser(key, this));
value->attach(attachable_ptr{new eraser(key, this)});
}
}
......@@ -97,7 +97,8 @@ void actor_registry::erase(actor_id key, std::uint32_t reason) {
auto i = m_entries.find(key);
if (i != m_entries.end()) {
auto& entry = i->second;
CPPA_LOG_INFO("erased " << key << ", reason = " << std::hex << reason);
CPPA_LOG_INFO("erased actor with ID " << key
<< ", reason " << reason);
entry.first = nullptr;
entry.second = reason;
}
......
......@@ -29,26 +29,20 @@
#include "cppa/any_tuple.hpp"
#include "cppa/singletons.hpp"
#include "cppa/detail/empty_tuple.hpp"
#include "cppa/detail/singleton_manager.hpp"
namespace cppa {
namespace {
inline detail::empty_tuple* s_empty_tuple() {
return detail::singleton_manager::get_empty_tuple();
}
} // namespace <anonymous>
any_tuple::any_tuple() : m_vals(s_empty_tuple()) { }
any_tuple::any_tuple() : m_vals(get_empty_tuple()) { }
any_tuple::any_tuple(detail::abstract_tuple* ptr) : m_vals(ptr) { }
any_tuple::any_tuple(any_tuple&& other) : m_vals(s_empty_tuple()) {
any_tuple::any_tuple(any_tuple&& other) : m_vals(get_empty_tuple()) {
m_vals.swap(other.m_vals);
}
any_tuple::any_tuple(const value_ptr& vals) : m_vals(vals) { }
any_tuple::any_tuple(const data_ptr& vals) : m_vals(vals) { }
any_tuple& any_tuple::operator=(any_tuple&& other) {
m_vals.swap(other.m_vals);
......@@ -56,7 +50,7 @@ any_tuple& any_tuple::operator=(any_tuple&& other) {
}
void any_tuple::reset() {
m_vals.reset(s_empty_tuple());
m_vals.reset(get_empty_tuple());
}
size_t any_tuple::size() const {
......
......@@ -32,7 +32,9 @@
namespace cppa {
attachable::~attachable() {
}
attachable::~attachable() { }
attachable::token::token(const std::type_info& tinfo, const void* vptr)
: subtype(tinfo), ptr(vptr) { }
} // namespace cppa::detail
......@@ -38,12 +38,37 @@
namespace cppa {
context_switching_actor::context_switching_actor()
: m_fiber(&context_switching_actor::trampoline, this) {
context_switching_actor::context_switching_actor(std::function<void()> fun)
: super(std::move(fun), actor_state::ready, true)
, m_fiber(&context_switching_actor::trampoline, this) { }
auto context_switching_actor::init_timeout(const util::duration& tout) -> timeout_type {
// request explicit timeout message
request_timeout(tout);
return {};
}
context_switching_actor::context_switching_actor(std::function<void()> fun)
: super(std::move(fun)), m_fiber(&context_switching_actor::trampoline, this) {
detail::recursive_queue_node* context_switching_actor::await_message(const timeout_type&) {
// receives requested timeout message if timeout occured
return await_message();
}
detail::recursive_queue_node* context_switching_actor::await_message() {
auto e = m_mailbox.try_pop();
while (e == nullptr) {
if (m_mailbox.can_fetch_more() == false) {
set_state(actor_state::about_to_block);
// make sure mailbox is empty
if (m_mailbox.can_fetch_more()) {
// someone preempt us => continue
set_state(actor_state::ready);
}
// wait until actor becomes rescheduled
else detail::yield(detail::yield_state::blocked);
}
e = m_mailbox.try_pop();
}
return e;
}
void context_switching_actor::trampoline(void* this_ptr) {
......@@ -64,31 +89,12 @@ void context_switching_actor::trampoline(void* this_ptr) {
detail::yield(detail::yield_state::done);
}
detail::recursive_queue_node* context_switching_actor::receive_node() {
detail::recursive_queue_node* e = m_mailbox.try_pop();
while (e == nullptr) {
if (m_mailbox.can_fetch_more() == false) {
m_state.store(abstract_scheduled_actor::about_to_block);
// make sure mailbox is empty
if (m_mailbox.can_fetch_more()) {
// someone preempt us => continue
m_state.store(abstract_scheduled_actor::ready);
}
else {
// wait until actor becomes rescheduled
detail::yield(detail::yield_state::blocked);
}
}
e = m_mailbox.try_pop();
}
return e;
}
scheduled_actor_type context_switching_actor::impl_type() {
return context_switching_impl;
}
resume_result context_switching_actor::resume(util::fiber* from, actor_ptr& next_job) {
CPPA_LOG_TRACE("id = " << id() << ", state = " << static_cast<int>(state()));
CPPA_REQUIRE(from != nullptr);
CPPA_REQUIRE(next_job == nullptr);
using namespace detail;
......@@ -107,15 +113,15 @@ resume_result context_switching_actor::resume(util::fiber* from, actor_ptr& next
CPPA_REQUIRE(next_job == nullptr);
m_chained_actor.swap(next_job);
CPPA_REQUIRE(m_chained_actor == nullptr);
switch (compare_exchange_state(abstract_scheduled_actor::about_to_block,
abstract_scheduled_actor::blocked)) {
case abstract_scheduled_actor::ready: {
switch (compare_exchange_state(actor_state::about_to_block,
actor_state::blocked)) {
case actor_state::ready: {
// restore variables
m_chained_actor.swap(next_job);
CPPA_REQUIRE(next_job == nullptr);
break;
}
case abstract_scheduled_actor::blocked: {
case actor_state::blocked: {
// wait until someone re-schedules that actor
return resume_result::actor_blocked;
}
......
......@@ -63,7 +63,7 @@ void default_actor_addressing::write(serializer* sink, const actor_ptr& ptr) {
else {
// local actor?
if (!ptr->is_proxy()) {
detail::singleton_manager::get_actor_registry()->put(ptr->id(), ptr);
get_actor_registry()->put(ptr->id(), ptr);
}
auto pinf = m_pinf;
if (ptr->is_proxy()) {
......@@ -101,7 +101,7 @@ actor_ptr default_actor_addressing::read(deserializer* source) {
// local actor?
auto pinf = process_information::get();
if (pid == pinf->process_id() && nid == pinf->node_id()) {
return detail::singleton_manager::get_actor_registry()->get(aid);
return get_actor_registry()->get(aid);
}
else {
process_information tmp(pid, nid);
......
......@@ -36,6 +36,7 @@
#include "cppa/detail/memory.hpp"
#include "cppa/detail/singleton_manager.hpp"
#include "cppa/detail/sync_request_bouncer.hpp"
using namespace std;
......@@ -54,13 +55,13 @@ default_actor_proxy::default_actor_proxy(actor_id mid,
: super(mid), m_proto(parent), m_pinf(pinfo) { }
default_actor_proxy::~default_actor_proxy() {
CPPA_LOG_TRACE("node = " << to_string(*m_pinf) << ", aid = " << id());
auto aid = id();
auto node = m_pinf;
auto proto = m_proto;
proto->run_later([aid, node, proto] {
CPPA_LOGF_TRACE("lambda from ~default_actor_proxy"
<< "; node = " << to_string(*node) << ", aid " << aid
CPPA_LOGC_TRACE("cppa::network::default_actor_proxy",
"~default_actor_proxy$run_later",
"node = " << to_string(*node) << ", aid " << aid
<< ", proto = " << to_string(proto->identifier()));
proto->addressing()->erase(*node, aid);
auto p = proto->get_peer(*node);
......@@ -94,10 +95,12 @@ void default_actor_proxy::forward_msg(const actor_ptr& sender,
switch (m_pending_requests.enqueue(new_req_info(sender, mid))) {
case intrusive::queue_closed: {
if (sender) {
intrusive_ptr<default_actor_proxy> _this{this};
auto rsn = exit_reason();
m_proto->run_later([_this,rsn,sender,mid] {
detail::sync_request_bouncer f{_this.get(), rsn};
m_proto->run_later([rsn,sender,mid] {
CPPA_LOGC_TRACE("cppa::network::default_actor_proxy",
"forward_msg$bouncer",
"bounce message for reason " << rsn);
detail::sync_request_bouncer f{rsn};
f(sender.get(), mid);
});
}
......@@ -110,13 +113,16 @@ void default_actor_proxy::forward_msg(const actor_ptr& sender,
auto node = m_pinf;
auto proto = m_proto;
m_proto->run_later([hdr, msg, node, proto] {
CPPA_LOGF_TRACE("lambda from default_actor_proxy::forward_msg");
CPPA_LOGC_TRACE("cppa::network::default_actor_proxy",
"forward_msg$forwarder",
"");
proto->enqueue(*node, hdr, msg);
});
}
void default_actor_proxy::enqueue(actor* sender, any_tuple msg) {
CPPA_LOG_TRACE(CPPA_ARG(sender) << ", " << CPPA_TARG(msg, to_string));
void default_actor_proxy::enqueue(const actor_ptr& sender, any_tuple msg) {
CPPA_LOG_TRACE(CPPA_TARG(sender, to_string) << ", "
<< CPPA_TARG(msg, to_string));
auto& arr = detail::static_types_array<atom_value, uint32_t>::arr;
if ( msg.size() == 2
&& msg.type_at(0) == arr[0]
......@@ -126,10 +132,11 @@ void default_actor_proxy::enqueue(actor* sender, any_tuple msg) {
intrusive_ptr<default_actor_proxy> _this{this};
auto reason = msg.get_as<uint32_t>(1);
m_proto->run_later([_this, reason] {
CPPA_LOGC_TRACE("cppa::network::default_actor_proxy",
"enqueue$kill_proxy_helper",
"KILL_PROXY with exit reason " << reason);
_this->cleanup(reason);
// make sure cleanup is done before closing requests queue
std::atomic_thread_fence(std::memory_order_seq_cst);
detail::sync_request_bouncer f{_this.get(), reason};
detail::sync_request_bouncer f{reason};
_this->m_pending_requests.close([&](const sync_request_info& e) {
f(e.sender.get(), e.mid);
});
......@@ -139,9 +146,12 @@ void default_actor_proxy::enqueue(actor* sender, any_tuple msg) {
forward_msg(sender, move(msg));
}
void default_actor_proxy::sync_enqueue(actor* sender, message_id_t mid, any_tuple msg) {
CPPA_LOG_TRACE(CPPA_ARG(sender) << ", " << CPPA_MARG(mid, integer_value)
<< ", " << CPPA_TARG(msg, to_string));
void default_actor_proxy::sync_enqueue(const actor_ptr& sender,
message_id_t mid,
any_tuple msg) {
CPPA_LOG_TRACE(CPPA_TARG(sender, to_string) << ", "
<< CPPA_MARG(mid, integer_value) << ", "
<< CPPA_TARG(msg, to_string));
forward_msg(sender, move(msg), mid);
}
......
......@@ -36,6 +36,7 @@
#include "cppa/match.hpp"
#include "cppa/logging.hpp"
#include "cppa/to_string.hpp"
#include "cppa/singletons.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/actor_proxy.hpp"
#include "cppa/binary_serializer.hpp"
......@@ -195,7 +196,7 @@ void default_peer::monitor(const actor_ptr&,
CPPA_LOG_ERROR("received MONITOR from invalid peer");
return;
}
auto entry = detail::singleton_manager::get_actor_registry()->get_entry(aid);
auto entry = get_actor_registry()->get_entry(aid);
auto pself = process_information::get();
if (*node == *pself) {
......@@ -221,7 +222,9 @@ void default_peer::monitor(const actor_ptr&,
default_protocol_ptr proto = m_parent;
entry.first->attach_functor([=](uint32_t reason) {
proto->run_later([=] {
CPPA_LOGF_TRACE("lambda from default_peer::monitor");
CPPA_LOGC_TRACE("cppa::network::default_peer",
"monitor$kill_proxy_helper",
"reason = " << reason);
auto p = proto->get_peer(*node);
if (p) p->enqueue(make_any_tuple(atom("KILL_PROXY"), pself, aid, reason));
});
......
......@@ -34,6 +34,7 @@
#include "cppa/logging.hpp"
#include "cppa/to_string.hpp"
#include "cppa/singletons.hpp"
#include "cppa/network/middleman.hpp"
#include "cppa/network/default_peer.hpp"
......@@ -88,11 +89,12 @@ void default_protocol::publish(const actor_ptr& whom,
if (!whom) return;
CPPA_REQUIRE(args.size() == 0);
static_cast<void>(args); // keep compiler happy
singleton_manager::get_actor_registry()->put(whom->id(), whom);
get_actor_registry()->put(whom->id(), whom);
default_protocol_ptr proto = this;
auto impl = make_counted<default_peer_acceptor>(this, move(ptr), whom);
run_later([=] {
CPPA_LOGF_TRACE("lambda from default_protocol::publish");
CPPA_LOGC_TRACE("cppa::network::default_protocol",
"publish$add_acceptor", "");
proto->m_acceptors[whom].push_back(impl);
proto->continue_reader(impl.get());
});
......@@ -102,11 +104,10 @@ void default_protocol::unpublish(const actor_ptr& whom) {
CPPA_LOG_TRACE("whom = " << to_string(whom));
default_protocol_ptr proto = this;
run_later([=] {
CPPA_LOGF_TRACE("lambda from default_protocol::unpublish");
CPPA_LOGC_TRACE("cppa::network::default_protocol",
"unpublish$remove_acceptors", "");
auto& acceptors = m_acceptors[whom];
for (auto& ptr : acceptors) {
proto->stop_reader(ptr.get());
}
for (auto& ptr : acceptors) proto->stop_reader(ptr.get());
m_acceptors.erase(whom);
});
}
......@@ -123,8 +124,12 @@ void default_protocol::register_peer(const process_information& node,
auto tmp = entry.queue->pop();
ptr->enqueue(tmp.first, tmp.second);
}
CPPA_LOG_INFO("peer " << to_string(node) << " added");
}
else {
CPPA_LOG_WARNING("peer " << to_string(node) << " already defined, "
"multiple calls to remote_actor()?");
}
else { CPPA_LOG_ERROR("peer " << to_string(node) << " already defined"); }
}
default_peer_ptr default_protocol::get_peer(const process_information& n) {
......@@ -192,12 +197,13 @@ actor_ptr default_protocol::remote_actor(io_stream_ptr_pair io,
std::cerr << "*** warning: remote_actor() called to access a local actor\n"
<< std::flush;
# endif
return singleton_manager::get_actor_registry()->get(remote_aid);
return get_actor_registry()->get(remote_aid);
}
default_protocol_ptr proto = this;
intrusive::blocking_single_reader_queue<remote_actor_result> q;
run_later([proto, io, pinfptr, remote_aid, &q] {
CPPA_LOGF_TRACE("lambda from default_protocol::remote_actor");
CPPA_LOGC_TRACE("cppa::network::default_protocol",
"remote_actor$create_connection", "");
auto pp = proto->get_peer(*pinfptr);
CPPA_LOGF_INFO_IF(pp, "connection already exists (re-use old one)");
if (!pp) proto->new_peer(io.first, io.second, pinfptr);
......
......@@ -33,7 +33,7 @@
namespace cppa { namespace detail {
empty_tuple::empty_tuple() : super(tuple_impl_info::statically_typed) { }
empty_tuple::empty_tuple() : super(false) { }
size_t empty_tuple::size() const {
return 0;
......
......@@ -31,14 +31,17 @@
#include <iostream>
#include "cppa/to_string.hpp"
#include "cppa/on.hpp"
#include "cppa/cppa.hpp"
#include "cppa/self.hpp"
#include "cppa/logging.hpp"
#include "cppa/event_based_actor.hpp"
namespace cppa {
class default_scheduled_actor : public event_based_actor {
typedef event_based_actor super;
public:
typedef std::function<void()> fun_type;
......@@ -48,6 +51,7 @@ class default_scheduled_actor : public event_based_actor {
void init() {
become (
on(atom("RUN")) >> [=] {
CPPA_LOGS_TRACE("init$lambda", "");
unbecome();
m_fun();
}
......@@ -68,7 +72,7 @@ intrusive_ptr<event_based_actor> event_based_actor::from(std::function<void()> f
return detail::memory::create<default_scheduled_actor>(std::move(fun));
}
event_based_actor::event_based_actor() : super(super::blocked) { }
event_based_actor::event_based_actor() : super(actor_state::blocked, true) { }
void event_based_actor::dequeue(behavior&) {
quit(exit_reason::unallowed_function_call);
......@@ -83,24 +87,14 @@ void event_based_actor::dequeue_response(behavior&, message_id_t) {
}
resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) {
# ifdef CPPA_DEBUG
auto st = m_state.load();
switch (st) {
case abstract_scheduled_actor::ready:
case abstract_scheduled_actor::pending:
// state ok
break;
default: {
std::cerr << "UNEXPECTED STATE: " << st << std::endl;
// failed requirement calls abort() and prints stack trace
CPPA_REQUIRE( st == abstract_scheduled_actor::ready
|| st == abstract_scheduled_actor::pending);
}
}
# endif // CPPA_DEBUG
CPPA_LOG_TRACE("id = " << id() << ", state = " << static_cast<int>(state()));
CPPA_REQUIRE( state() == actor_state::ready
|| state() == actor_state::pending);
scoped_self_setter sss{this};
auto done_cb = [&]() {
m_state.store(abstract_scheduled_actor::done);
CPPA_LOG_TRACE("");
if (exit_reason() == exit_reason::not_exited) quit(exit_reason::normal);
set_state(actor_state::done);
m_bhvr_stack.clear();
m_bhvr_stack.cleanup();
on_exit();
......@@ -109,54 +103,81 @@ resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) {
};
CPPA_REQUIRE(next_job == nullptr);
try {
detail::recursive_queue_node* e = nullptr;
for (;;) {
e = m_mailbox.try_pop();
//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_LOG_DEBUG("no more element in mailbox; going to block");
next_job.swap(m_chained_actor);
m_state.store(abstract_scheduled_actor::about_to_block);
set_state(actor_state::about_to_block);
std::atomic_thread_fence(std::memory_order_seq_cst);
if (m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state(
abstract_scheduled_actor::about_to_block,
abstract_scheduled_actor::blocked)) {
case abstract_scheduled_actor::ready:
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);
next_job.swap(m_chained_actor);
CPPA_LOG_DEBUG("switched back to ready: "
"interrupted by arriving message");
break;
case abstract_scheduled_actor::blocked:
case actor_state::blocked:
CPPA_LOG_DEBUG("set state successfully to blocked");
// done setting actor to blocked
return resume_result::actor_blocked;
case abstract_scheduled_actor::pending:
CPPA_CRITICAL("illegal state: pending");
case abstract_scheduled_actor::done:
CPPA_CRITICAL("illegal state: done");
case abstract_scheduled_actor::about_to_block:
CPPA_CRITICAL("illegal state: about_to_block");
default:
CPPA_LOG_ERROR("invalid state");
CPPA_CRITICAL("invalid state");
};
}
else {
m_state.store(abstract_scheduled_actor::ready);
CPPA_LOG_DEBUG("switched back to ready: "
"mailbox can fetch more");
set_state(actor_state::ready);
CPPA_REQUIRE(m_chained_actor == nullptr);
next_job.swap(m_chained_actor);
}
}
else if (m_bhvr_stack.invoke(m_policy, this, e)) {
if (m_bhvr_stack.empty()) {
done_cb();
return resume_result::actor_done;
else {
CPPA_LOG_DEBUG("try to invoke message: " << to_string(e->msg));
if (m_bhvr_stack.invoke(m_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()) {
CPPA_LOG_DEBUG("behavior stack empty");
done_cb();
return resume_result::actor_done;
}
m_bhvr_stack.cleanup();
}
m_bhvr_stack.cleanup();
}
}
}
catch (actor_exited& what) { cleanup(what.reason()); }
catch (...) { cleanup(exit_reason::unhandled_exception); }
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;
}
......@@ -181,12 +202,19 @@ void event_based_actor::become_waiting_for(behavior&& bhvr, message_id_t mf) {
}
void event_based_actor::quit(std::uint32_t reason) {
if (reason == exit_reason::normal) {
cleanup(exit_reason::normal);
m_bhvr_stack.clear();
}
else {
abstract_scheduled_actor::quit(reason);
CPPA_LOG_TRACE("reason = " << reason
<< ", class " << detail::demangle(typeid(*this)));
cleanup(reason);
m_bhvr_stack.clear();
if (reason == exit_reason::unallowed_function_call) {
CPPA_LOG_WARNING("actor tried to use a blocking function");
// when using receive(), the non-blocking nature of event-based
// actors breaks any assumption the user has about his code,
// in particular, receive_loop() is a deadlock when not throwing
// an exception here
aout << "*** warning: event-based actor killed because it tried to "
"use receive()\n";
throw actor_exited(reason);
}
}
......
......@@ -31,6 +31,7 @@
#include "cppa/cppa.hpp"
#include "cppa/group.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/singletons.hpp"
#include "cppa/util/shared_spinlock.hpp"
#include "cppa/util/shared_lock_guard.hpp"
#include "cppa/util/upgrade_lock_guard.hpp"
......@@ -40,29 +41,21 @@
namespace cppa {
namespace {
inline detail::group_manager* manager() {
return detail::singleton_manager::get_group_manager();
}
} // <anonymous>
intrusive_ptr<group> group::get(const std::string& arg0,
const std::string& arg1) {
return manager()->get(arg0, arg1);
return get_group_manager()->get(arg0, arg1);
}
intrusive_ptr<group> group::anonymous() {
return manager()->anonymous();
return get_group_manager()->anonymous();
}
void group::add_module(group::unique_module_ptr ptr) {
manager()->add_module(std::move(ptr));
get_group_manager()->add_module(std::move(ptr));
}
group::module_ptr group::get_module(const std::string& module_name) {
return manager()->get_module(module_name);
return get_group_manager()->get_module(module_name);
}
group::subscription::subscription(const channel_ptr& s,
......@@ -108,7 +101,7 @@ struct group_nameserver : event_based_actor {
};
void publish_local_groups_at(std::uint16_t port, const char* addr) {
auto gn = spawn<group_nameserver, hidden>();
auto gn = spawn<group_nameserver,hidden>();
try {
publish(gn, port, addr);
}
......
......@@ -66,14 +66,14 @@ class local_group : public group {
public:
void send_all_subscribers(actor* sender, const any_tuple& msg) {
void send_all_subscribers(const actor_ptr& sender, const any_tuple& msg) {
shared_guard guard(m_mtx);
for (auto& s : m_subscribers) {
s->enqueue(sender, msg);
}
}
void enqueue(actor* sender, any_tuple msg) {
void enqueue(const actor_ptr& sender, any_tuple msg) {
send_all_subscribers(sender, msg);
m_broker->enqueue(sender, move(msg));
}
......@@ -189,7 +189,7 @@ class local_group_proxy : public local_group {
CPPA_REQUIRE(remote_broker != nullptr);
CPPA_REQUIRE(remote_broker->is_proxy());
m_broker = move(remote_broker);
m_proxy_broker = spawn<proxy_broker, hidden>(this);
m_proxy_broker = spawn<proxy_broker,hidden>(this);
}
group::subscription subscribe(const channel_ptr& who) {
......@@ -215,7 +215,7 @@ class local_group_proxy : public local_group {
}
}
void enqueue(actor* sender, any_tuple msg) {
void enqueue(const actor_ptr& sender, any_tuple msg) {
// forward message to the broker
m_broker->enqueue(sender,
make_any_tuple(atom("FORWARD"), move(msg)));
......@@ -342,7 +342,7 @@ class remote_group : public group {
void unsubscribe(const channel_ptr&) { /* never called */ }
void enqueue(actor* sender, any_tuple msg) {
void enqueue(const actor_ptr& sender, any_tuple msg) {
m_decorated->enqueue(sender, msg);
}
......@@ -426,8 +426,11 @@ class remote_group_module : public group::module {
auto sm = make_counted<shared_map>();
group::module_ptr _this = this;
m_map = sm;
auto worker = spawn<hidden>([_this, sm] {
typedef map<string, pair<actor_ptr, vector<pair<string, remote_group_ptr>>>>
auto worker = spawn<blocking_api+hidden>([_this, sm] {
CPPA_LOGC_TRACE(detail::demangle(typeid(*_this)),
"remote_group_module$worker",
"");
typedef map<string,pair<actor_ptr,vector<pair<string,remote_group_ptr>>>>
peer_map;
peer_map peers;
receive_loop (
......
......@@ -30,8 +30,10 @@
#include "cppa/cppa.hpp"
#include "cppa/atom.hpp"
#include "cppa/logging.hpp"
#include "cppa/scheduler.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/message_future.hpp"
namespace cppa {
......@@ -74,7 +76,7 @@ local_actor::local_actor(bool sflag)
, m_is_scheduled(sflag), m_dummy_node(), m_current_node(&m_dummy_node) { }
void local_actor::monitor(actor_ptr whom) {
if (whom) whom->attach(new down_observer(this, whom));
if (whom) whom->attach(attachable_ptr{new down_observer(this, whom)});
}
void local_actor::demonitor(actor_ptr whom) {
......@@ -111,7 +113,7 @@ void local_actor::reply_message(any_tuple&& what) {
}
auto& id = m_current_node->mid;
if (id.valid() == false || id.is_response()) {
send_message(whom.get(), std::move(what));
send_message(whom, std::move(what));
}
else if (!id.is_answered()) {
if (chaining_enabled()) {
......@@ -149,7 +151,7 @@ response_handle local_actor::make_response_handle() {
return std::move(result);
}
message_id_t local_actor::send_timed_sync_message(actor* whom,
message_id_t local_actor::send_timed_sync_message(const actor_ptr& whom,
const util::duration& rel_time,
any_tuple&& what) {
auto mid = this->send_sync_message(whom, std::move(what));
......@@ -166,4 +168,9 @@ sync_handle_helper local_actor::handle_response(const message_future& mf) {
return ::cppa::handle_response(mf);
}
void local_actor::cleanup(std::uint32_t reason) {
m_subscriptions.clear();
super::cleanup(reason);
}
} // namespace cppa
......@@ -33,6 +33,8 @@
#include <fstream>
#include <algorithm>
#include <execinfo.h> //DEL_ME
#ifndef CPPA_WINDOWS
#include <unistd.h>
#include <sys/types.h>
......@@ -70,11 +72,11 @@ class logging_impl : public logging {
void initialize() {
m_thread = thread([this] { (*this)(); });
log("TRACE ", "logging", "run", __FILE__, __LINE__, "ENTRY");
log("TRACE", "logging", "run", __FILE__, __LINE__, "ENTRY");
}
void destroy() {
log("TRACE ", "logging", "run", __FILE__, __LINE__, "EXIT");
log("TRACE", "logging", "run", __FILE__, __LINE__, "EXIT");
// an empty string means: shut down
m_queue.push_back(new log_event{0, ""});
m_thread.join();
......@@ -101,18 +103,19 @@ class logging_impl : public logging {
const char* function_name,
const char* c_full_file_name,
int line_num,
const std::string &msg) {
const std::string& msg) {
string class_name = c_class_name;
replace_all(class_name, "::", ".");
replace_all(class_name, "(anonymous namespace)", "$anon$");
string file_name;
string full_file_name = c_full_file_name;
auto i = find(full_file_name.rbegin(), full_file_name.rend(), '/').base();
if (i == full_file_name.end()) {
file_name = move(full_file_name);
}
else {
file_name = string(i, full_file_name.end());
auto ri = find(full_file_name.rbegin(), full_file_name.rend(), '/');
if (ri != full_file_name.rend()) {
auto i = ri.base();
if (i == full_file_name.end()) file_name = move(full_file_name);
else file_name = string(i, full_file_name.end());
}
else file_name = move(full_file_name);
ostringstream line;
line << time(0) << " "
<< level << " "
......@@ -134,9 +137,23 @@ class logging_impl : public logging {
} // namespace <anonymous>
logging::~logging() { }
logging::trace_helper::trace_helper(std::string class_name,
const char* fun_name,
const char* file_name,
int line_num,
const std::string& msg)
: m_class(std::move(class_name)), m_fun_name(fun_name)
, m_file_name(file_name), m_line_num(line_num) {
get_logger()->log("TRACE", m_class.c_str(), fun_name,
file_name, line_num, "ENTRY " + msg);
}
logging* logging::instance() { return detail::singleton_manager::get_logger(); }
logging::trace_helper::~trace_helper() {
get_logger()->log("TRACE", m_class.c_str(), m_fun_name,
m_file_name, m_line_num, "EXIT");
}
logging::~logging() { }
logging* logging::create_singleton() { return new logging_impl; }
......
......@@ -427,14 +427,11 @@ class middleman_impl : public abstract_middleman {
}
void run_later(function<void()> fun) {
CPPA_LOG_TRACE("");
m_queue.enqueue(new middleman_event(move(fun)));
atomic_thread_fence(memory_order_seq_cst);
uint8_t dummy = 0;
if (write(m_pipe_write, &dummy, sizeof(dummy)) != sizeof(dummy)) {
// already exited?
CPPA_LOG_WARNING("cannot write to pipe");
}
// ignore result; write error only means middleman already exited
static_cast<void>(write(m_pipe_write, &dummy, sizeof(dummy)));
}
protected:
......@@ -453,10 +450,9 @@ class middleman_impl : public abstract_middleman {
void destroy() {
run_later([this] {
CPPA_LOG_TRACE("lambda from middleman_impl::stop");
CPPA_LOGM_TRACE("destroy$helper", "");
this->m_done = true;
});
//enqueue_message(middleman_message::create());
m_thread.join();
close(m_pipe_read);
close(m_pipe_write);
......
......@@ -32,8 +32,7 @@
namespace cppa { namespace detail {
object_array::object_array() : super(tuple_impl_info::dynamically_typed) {
}
object_array::object_array() : super(true) { }
void object_array::push_back(const object& what) {
m_elements.push_back(what);
......
......@@ -30,14 +30,11 @@
#include "cppa/detail/recursive_queue_node.hpp"
using std::move;
namespace cppa { namespace detail {
recursive_queue_node::recursive_queue_node(actor_ptr sptr,
recursive_queue_node::recursive_queue_node(const actor_ptr& sptr,
any_tuple data,
message_id_t id)
: next(nullptr), marked(false), sender(move(sptr))
, msg(move(data)), mid(id) { }
: next(nullptr), marked(false), sender(sptr), msg(std::move(data)), mid(id) { }
} } // namespace cppa::detail
......@@ -56,14 +56,23 @@ bool response_handle::synchronous() const {
void response_handle::apply(any_tuple msg) const {
if (valid()) {
local_actor* sptr = self.unchecked();
if (sptr && sptr == m_from) {
if (sptr->chaining_enabled()) {
if (m_to->chained_sync_enqueue(sptr, m_id, move(msg))) {
bool use_chaining = sptr && sptr == m_from && sptr->chaining_enabled();
if (synchronous()) {
if (use_chaining) {
if (m_to->chained_sync_enqueue(m_from, m_id, move(msg))) {
sptr->chained_actor(m_to);
}
}
else m_to->sync_enqueue(m_from, m_id, move(msg));
}
else {
if (use_chaining) {
if (m_to->chained_enqueue(m_from, move(msg))) {
sptr->chained_actor(m_to);
}
}
else m_to->enqueue(m_from, move(msg));
}
else m_to->enqueue(m_from.get(), move(msg));
}
}
......
......@@ -32,13 +32,14 @@
#include "cppa/scheduler.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/sync_request_bouncer.hpp"
namespace cppa {
scheduled_actor::scheduled_actor(bool enable_chained_send)
: local_actor(enable_chained_send), next(0), m_scheduler(0), m_hidden(false) { }
scheduled_actor::scheduled_actor(actor_state init_state, bool chained_send)
: super(chained_send), next(nullptr), m_has_pending_tout(false)
, m_pending_tout(0), m_state(init_state), m_scheduler(nullptr)
, m_hidden(false) { }
void scheduled_actor::attach_to_scheduler(scheduler* sched, bool hidden) {
CPPA_REQUIRE(sched != nullptr);
......@@ -54,8 +55,122 @@ void scheduled_actor::attach_to_scheduler(scheduler* sched, bool hidden) {
m_scheduler = sched;
}
bool scheduled_actor::initialized() {
bool scheduled_actor::initialized() const {
return m_scheduler != nullptr;
}
void scheduled_actor::request_timeout(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
auto e = this->new_mailbox_element(this, std::move(msg));
this->m_mailbox.enqueue(e);
}
else get_scheduler()->delayed_send(this, d, std::move(msg));
m_has_pending_tout = true;
}
}
scheduled_actor::~scheduled_actor() {
if (!m_mailbox.closed()) {
detail::sync_request_bouncer f{exit_reason()};
m_mailbox.close(f);
}
}
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(actor_state next_state,
bool* failed,
mailbox_element* e) {
CPPA_REQUIRE( next_state == actor_state::ready
|| next_state == actor_state::pending);
CPPA_REQUIRE(e->marked == false);
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_LOG_DEBUG("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 (failed) *failed = true;
break;
}
default: break;
}
return false;
}
bool scheduled_actor::sync_enqueue_impl(actor_state next,
const actor_ptr& sender,
any_tuple& msg,
message_id_t id) {
bool err = false;
auto ptr = new_mailbox_element(sender, std::move(msg), id);
bool res = enqueue(next, &err, ptr);
if (err) {
detail::sync_request_bouncer f{exit_reason()};
f(sender, id);
CPPA_REQUIRE(res == false);
}
return res;
}
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 actor_ptr& sender, any_tuple msg) {
enqueue_impl(actor_state::ready, sender, std::move(msg));
}
bool scheduled_actor::chained_enqueue(const actor_ptr& sender, any_tuple msg) {
return enqueue_impl(actor_state::pending, sender, std::move(msg));
}
void scheduled_actor::sync_enqueue(const actor_ptr& sender,
message_id_t id,
any_tuple msg) {
sync_enqueue_impl(actor_state::ready, sender, msg, id);
}
bool scheduled_actor::chained_sync_enqueue(const actor_ptr& sender,
message_id_t id,
any_tuple msg) {
return sync_enqueue_impl(actor_state::pending, sender, msg, id);
}
} // namespace cppa
......@@ -32,30 +32,26 @@
namespace cppa { namespace detail {
scheduled_actor_dummy::scheduled_actor_dummy()
: scheduled_actor(actor_state::blocked, false) { }
void scheduled_actor_dummy::enqueue(const actor_ptr&, any_tuple) { }
void scheduled_actor_dummy::quit(std::uint32_t) { }
void scheduled_actor_dummy::dequeue(behavior&) { }
void scheduled_actor_dummy::dequeue(partial_function&) { }
void scheduled_actor_dummy::dequeue_response(behavior&, message_id_t) { }
void scheduled_actor_dummy::link_to(const intrusive_ptr<actor>&) { }
void scheduled_actor_dummy::unlink_from(const intrusive_ptr<actor>&) { }
void scheduled_actor_dummy::detach(const attachable::token&) { }
bool scheduled_actor_dummy::attach(attachable*) { return false; }
void scheduled_actor_dummy::do_become(behavior&&, bool) { }
void scheduled_actor_dummy::become_waiting_for(behavior&&, message_id_t) { }
bool scheduled_actor_dummy::has_behavior() { return false; }
void scheduled_actor_dummy::sync_enqueue(const actor_ptr&,
message_id_t,
any_tuple) { }
resume_result scheduled_actor_dummy::resume(util::fiber*,actor_ptr&) {
return resume_result::actor_blocked;
}
bool scheduled_actor_dummy::establish_backlink(const intrusive_ptr<actor>&) {
return false;
}
bool scheduled_actor_dummy::remove_backlink(const intrusive_ptr<actor>&) {
return false;
}
scheduled_actor_type scheduled_actor_dummy::impl_type() {
return event_based_impl;
}
......
......@@ -36,6 +36,7 @@
#include "cppa/on.hpp"
#include "cppa/self.hpp"
#include "cppa/receive.hpp"
#include "cppa/logging.hpp"
#include "cppa/anything.hpp"
#include "cppa/to_string.hpp"
#include "cppa/scheduler.hpp"
......@@ -43,7 +44,7 @@
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/context_switching_actor.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/singleton_manager.hpp"
#include "cppa/detail/thread_pool_scheduler.hpp"
......@@ -53,49 +54,29 @@ namespace cppa { namespace {
typedef std::uint32_t ui32;
typedef std::chrono::high_resolution_clock hrc;
struct exit_observer : cppa::attachable {
~exit_observer() {
cppa::detail::dec_actor_count();
}
void actor_exited(std::uint32_t) {
}
bool matches(const token&) {
return false;
}
~exit_observer() { get_actor_registry()->dec_running(); }
void actor_exited(std::uint32_t) { }
bool matches(const token&) { return false; }
};
inline decltype(std::chrono::high_resolution_clock::now()) now() {
return std::chrono::high_resolution_clock::now();
}
void async_send_impl(channel* to, actor* from, any_tuple&& msg) {
to->enqueue(from, move(msg));
}
void sync_reply_impl(actor* to, actor* from, message_id_t id, any_tuple&& msg) {
to->sync_enqueue(from, id, move(msg));
}
typedef void (*async_send_fun)(channel*, actor*, any_tuple&&);
typedef void (*sync_reply_fun)(actor*, actor*, message_id_t, any_tuple&&);
class delayed_msg {
public:
delayed_msg(async_send_fun arg0,
const channel_ptr& arg1,
const actor_ptr& arg2,
delayed_msg(const channel_ptr& arg0,
const actor_ptr& arg1,
message_id_t,
any_tuple&& arg4)
: fun(arg0), ptr_a(arg1), from(arg2), msg(move(arg4)) { }
any_tuple&& arg3)
: ptr_a(arg0), from(arg1), msg(move(arg3)) { }
delayed_msg(sync_reply_fun arg0,
delayed_msg(const actor_ptr& arg0,
const actor_ptr& arg1,
const actor_ptr& arg2,
message_id_t arg3,
any_tuple&& arg4)
: fun(arg0), ptr_b(arg1), from(arg2), id(arg3), msg(move(arg4)) { }
message_id_t arg2,
any_tuple&& arg3)
: ptr_b(arg0), from(arg1), id(arg2), msg(move(arg3)) { }
delayed_msg(delayed_msg&&) = default;
delayed_msg(const delayed_msg&) = default;
......@@ -103,14 +84,13 @@ class delayed_msg {
delayed_msg& operator=(const delayed_msg&) = default;
inline void eval() {
if (fun.is_left()) (fun.left())(ptr_a.get(), from.get(), move(msg));
else (fun.right())(ptr_b.get(), from.get(), id, move(msg));
CPPA_REQUIRE(ptr_a || ptr_b);
if (ptr_a) ptr_a->enqueue(from, move(msg));
else ptr_b->sync_enqueue(from, id, move(msg));
}
private:
either<async_send_fun,sync_reply_fun> fun;
channel_ptr ptr_a;
actor_ptr ptr_b;
actor_ptr from;
......@@ -128,14 +108,14 @@ class scheduler_helper {
typedef intrusive_ptr<thread_mapped_actor> ptr_type;
void start() {
ptr_type mtimer{detail::memory::create<thread_mapped_actor>()};
ptr_type mprinter{detail::memory::create<thread_mapped_actor>()};
ptr_type mt{detail::memory::create<thread_mapped_actor>()};
ptr_type mp{detail::memory::create<thread_mapped_actor>()};
// launch threads
m_timer_thread = std::thread(&scheduler_helper::timer_loop, mtimer);
m_printer_thread = std::thread(&scheduler_helper::printer_loop, mprinter);
m_timer_thread = std::thread{&scheduler_helper::timer_loop, mt};
m_printer_thread = std::thread{&scheduler_helper::printer_loop, mp};
// set member variables
m_timer = mtimer;
m_printer = mprinter;
m_timer = std::move(mt);
m_printer = std::move(mp);
}
void stop() {
......@@ -160,47 +140,41 @@ class scheduler_helper {
};
template<class Map, typename Fun, class T>
void insert_dmsg(Map& storage,
template<class Map, class T>
inline void insert_dmsg(Map& storage,
const util::duration& d,
Fun fun_ptr,
const T& to,
const actor_ptr& sender,
message_id_t id,
any_tuple&& tup ) {
auto tout = now();
any_tuple&& tup,
message_id_t id = message_id_t{}) {
auto tout = hrc::now();
tout += d;
delayed_msg dmsg{fun_ptr, to, sender, id, move(tup)};
delayed_msg dmsg{to, sender, id, move(tup)};
storage.insert(std::make_pair(std::move(tout), std::move(dmsg)));
}
void scheduler_helper::timer_loop(scheduler_helper::ptr_type m_self) {
typedef detail::abstract_actor<local_actor> impl_type;
typedef std::unique_ptr<detail::recursive_queue_node,detail::disposer> queue_node_ptr;
// setup & local variables
self.set(m_self.get());
auto& queue = m_self->mailbox();
std::multimap<decltype(now()), delayed_msg> messages;
queue_node_ptr msg_ptr;
//decltype(queue.pop()) msg_ptr = nullptr;
decltype(now()) tout;
bool done = false;
auto& queue = m_self->m_mailbox;
std::unique_ptr<detail::recursive_queue_node,detail::disposer> msg_ptr;
auto tout = hrc::now();
std::multimap<decltype(tout),delayed_msg> messages;
// message handling rules
auto mfun = (
on(atom("SEND"), arg_match) >> [&](const util::duration& d,
const channel_ptr& ptr,
any_tuple& tup) {
insert_dmsg(messages, d, async_send_impl,
ptr, msg_ptr->sender, message_id_t(), move(tup));
insert_dmsg(messages, d, ptr, msg_ptr->sender, move(tup));
},
on(atom("REPLY"), arg_match) >> [&](const util::duration& d,
const actor_ptr& ptr,
message_id_t id,
any_tuple& tup) {
insert_dmsg(messages, d, sync_reply_impl,
ptr, msg_ptr->sender, id, move(tup));
insert_dmsg(messages, d, ptr, msg_ptr->sender, move(tup), id);
},
on<atom("DIE")>() >> [&]() {
on(atom("DIE")) >> [&] {
done = true;
},
others() >> [&]() {
......@@ -214,11 +188,9 @@ void scheduler_helper::timer_loop(scheduler_helper::ptr_type m_self) {
// loop
while (!done) {
while (!msg_ptr) {
if (messages.empty()) {
msg_ptr.reset(queue.pop());
}
if (messages.empty()) msg_ptr.reset(queue.pop());
else {
tout = now();
tout = hrc::now();
// handle timeouts (send messages)
auto it = messages.begin();
while (it != messages.end() && (it->first) <= tout) {
......@@ -302,6 +274,7 @@ void scheduler::initialize() {
}
void scheduler::destroy() {
CPPA_LOG_TRACE("");
m_helper->stop();
delete this;
}
......@@ -310,19 +283,19 @@ scheduler::~scheduler() {
delete m_helper;
}
channel* scheduler::delayed_send_helper() {
return m_helper->m_timer.get();
const actor_ptr& scheduler::delayed_send_helper() {
return m_helper->m_timer;
}
void scheduler::register_converted_context(actor* what) {
if (what) {
detail::inc_actor_count();
what->attach(new exit_observer);
get_actor_registry()->inc_running();
what->attach(attachable_ptr{new exit_observer});
}
}
attachable* scheduler::register_hidden_context() {
detail::inc_actor_count();
get_actor_registry()->inc_running();
return new exit_observer;
}
......@@ -336,10 +309,6 @@ void set_default_scheduler(size_t num_threads) {
set_scheduler(new detail::thread_pool_scheduler(num_threads));
}
scheduler* get_scheduler() {
return detail::singleton_manager::get_scheduler();
}
scheduler* scheduler::create_singleton() {
return new detail::thread_pool_scheduler;
}
......
......@@ -40,7 +40,6 @@
#include "cppa/network/middleman.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/empty_tuple.hpp"
#include "cppa/detail/group_manager.hpp"
#include "cppa/detail/actor_registry.hpp"
......@@ -74,15 +73,23 @@ void singleton_manager::shutdown() {
}
//auto rptr = s_actor_registry.load();
//if (rptr) rptr->await_running_count_equal(0);
CPPA_LOGF_DEBUG("shutdown scheduler");
destroy(s_scheduler);
CPPA_LOGF_DEBUG("shutdown middleman");
destroy(s_middleman);
std::atomic_thread_fence(std::memory_order_seq_cst);
// it's safe to delete all other singletons now
CPPA_LOGF_DEBUG("close actor registry");
destroy(s_actor_registry);
CPPA_LOGF_DEBUG("shutdown group manager");
destroy(s_group_manager);
CPPA_LOGF_DEBUG("destroy empty tuple singleton");
destroy(s_empty_tuple);
CPPA_LOGF_DEBUG("clear type info map");
destroy(s_uniform_type_info_map);
CPPA_LOGF_DEBUG("clear decorated names log");
destroy(s_decorated_names_map);
CPPA_LOGF_DEBUG("shutdown logger");
destroy(s_logger);
}
......
......@@ -43,35 +43,49 @@
namespace cppa {
thread_mapped_actor::thread_mapped_actor() : m_initialized(true) { }
thread_mapped_actor::thread_mapped_actor() : super(std::function<void()>{}), m_initialized(true) { }
thread_mapped_actor::thread_mapped_actor(std::function<void()> fun)
: super(std::move(fun)), m_initialized(false) { }
void thread_mapped_actor::quit(std::uint32_t reason) {
cleanup(reason);
// actor_exited should not be catched, but if anyone does,
// self must point to a newly created instance
//self.set(nullptr);
throw actor_exited(reason);
auto thread_mapped_actor::init_timeout(const util::duration& rel_time) -> timeout_type {
auto result = std::chrono::high_resolution_clock::now();
result += rel_time;
return result;
}
void thread_mapped_actor::enqueue(actor* sender, any_tuple msg) {
m_mailbox.push_back(fetch_node(sender, std::move(msg)));
void thread_mapped_actor::enqueue(const actor_ptr& sender, any_tuple msg) {
auto ptr = new_mailbox_element(sender, std::move(msg));
this->m_mailbox.push_back(ptr);
}
void thread_mapped_actor::sync_enqueue(actor* sender,
void thread_mapped_actor::sync_enqueue(const actor_ptr& sender,
message_id_t id,
any_tuple msg ) {
if (!m_mailbox.push_back(fetch_node(sender, std::move(msg), id))) {
detail::sync_request_bouncer f{this, exit_reason()};
any_tuple msg) {
auto ptr = this->new_mailbox_element(sender, std::move(msg), id);
if (!this->m_mailbox.push_back(ptr)) {
detail::sync_request_bouncer f{this->exit_reason()};
f(sender, id);
}
}
bool thread_mapped_actor::initialized() {
detail::recursive_queue_node* thread_mapped_actor::await_message() {
return m_mailbox.pop();
}
detail::recursive_queue_node* thread_mapped_actor::await_message(const timeout_type& abs_time) {
return m_mailbox.try_pop(abs_time);
}
bool thread_mapped_actor::initialized() const {
return m_initialized;
}
void thread_mapped_actor::cleanup(std::uint32_t reason) {
detail::sync_request_bouncer f{reason};
m_mailbox.close(f);
super::cleanup(reason);
}
} // namespace cppa::detail
......@@ -35,11 +35,12 @@
#include <iostream>
#include "cppa/on.hpp"
#include "cppa/logging.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/context_switching_actor.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/thread_pool_scheduler.hpp"
using std::cout;
......@@ -92,24 +93,33 @@ struct thread_pool_scheduler::worker {
}
void operator()() {
CPPA_LOG_TRACE("");
util::fiber fself;
job_ptr job = nullptr;
actor_ptr next;
for (;;) {
aggressive(job) || moderate(job) || relaxed(job);
CPPA_LOG_DEBUG("dequeued new job");
if (job == m_dummy) {
CPPA_LOG_DEBUG("received dummy (quit)");
// dummy of doom received ...
m_job_queue->push_back(job); // kill the next guy
return; // and say goodbye
}
do {
next.reset();
CPPA_LOG_DEBUG("resume actor with ID " << job->id());
CPPA_REQUIRE(next == nullptr);
if (job->resume(&fself, next) == resume_result::actor_done) {
CPPA_LOG_DEBUG("actor is done");
bool hidden = job->is_hidden();
job->deref();
if (!hidden) dec_actor_count();
if (!hidden) get_actor_registry()->dec_running();
}
if (next) {
CPPA_LOG_DEBUG("got new job trough chaining");
job = static_cast<job_ptr>(next.get());
next.reset();
}
if (next) job = static_cast<job_ptr>(next.get());
else job = nullptr;
}
while (job); // loops until next == nullptr
......@@ -151,17 +161,20 @@ void thread_pool_scheduler::initialize() {
}
void thread_pool_scheduler::destroy() {
CPPA_LOG_TRACE("");
m_queue.push_back(&m_dummy);
CPPA_LOG_DEBUG("join supervisor");
m_supervisor.join();
// make sure job queue is empty, because destructor of m_queue would
// otherwise delete elements it shouldn't
CPPA_LOG_DEBUG("flush queue");
auto ptr = m_queue.try_pop();
while (ptr != nullptr) {
if (ptr != &m_dummy) {
bool hidden = ptr->is_hidden();
ptr->deref();
std::atomic_thread_fence(std::memory_order_seq_cst);
if (!hidden) dec_actor_count();
if (!hidden) get_actor_registry()->dec_running();
}
ptr = m_queue.try_pop();
}
......@@ -174,14 +187,14 @@ void thread_pool_scheduler::enqueue(scheduled_actor* what) {
template<typename F>
void exec_as_thread(bool is_hidden, local_actor_ptr p, F f) {
if (!is_hidden) { inc_actor_count(); }
if (!is_hidden) get_actor_registry()->inc_running();
std::thread([=] {
scoped_self_setter sss(p.get());
try { f(); }
catch (...) { }
if (!is_hidden) {
std::atomic_thread_fence(std::memory_order_seq_cst);
dec_actor_count();
get_actor_registry()->dec_running();
}
}).detach();
}
......@@ -198,7 +211,7 @@ actor_ptr thread_pool_scheduler::exec(spawn_options os, scheduled_actor_ptr p) {
}
p->attach_to_scheduler(this, is_hidden);
if (p->has_behavior()) {
if (!is_hidden) { inc_actor_count(); }
if (!is_hidden) get_actor_registry()->inc_running();
p->ref(); // implicit reference that's released if actor dies
switch (p->impl_type()) {
case default_event_based_impl: {
......
......@@ -16,6 +16,7 @@ namespace {
size_t s_pongs = 0;
behavior ping_behavior(size_t num_pings) {
CPPA_LOGF_TRACE("");
return (
on(atom("pong"), arg_match) >> [num_pings](int value) {
CPPA_LOGF_ERROR_IF(!self->last_sender(), "last_sender() == nullptr");
......@@ -38,6 +39,7 @@ behavior ping_behavior(size_t num_pings) {
}
behavior pong_behavior() {
CPPA_LOGF_TRACE("");
return (
on<atom("ping"), int>() >> [](int value) {
//cout << to_string(self->last_dequeued()) << endl;
......@@ -67,11 +69,9 @@ void ping(size_t num_pings) {
actor_ptr spawn_event_based_ping(size_t num_pings) {
CPPA_LOGF_TRACE("num_pings = " << num_pings);
s_pongs = 0;
return factory::event_based(
[num_pings] {
self->become(ping_behavior(num_pings));
}
).spawn();
return spawn([=] {
become(ping_behavior(num_pings));
});
}
void pong(actor_ptr ping_actor) {
......
......@@ -33,13 +33,12 @@ const char* cppa_strip_path(const char* fname) {
}
void cppa_unexpected_message(const char* fname, size_t line_num) {
CPPA_FORMAT_STR(std::cerr, fname, line_num,
"ERROR: unexpected message: "
<< to_string(self->last_dequeued()));
CPPA_PRINTERRC(fname, line_num,
"unexpected message: " << to_string(self->last_dequeued()));
}
void cppa_unexpected_timeout(const char* fname, size_t line_num) {
CPPA_FORMAT_STR(std::cerr, fname, line_num, "ERROR: unexpected timeout");
CPPA_PRINTERRC(fname, line_num, "unexpected timeout");
}
std::vector<std::string> split(const std::string& str, char delim) {
......
......@@ -9,6 +9,7 @@
#include <type_traits>
#include "cppa/actor.hpp"
#include "cppa/logging.hpp"
#include "cppa/to_string.hpp"
#include "cppa/util/scope_guard.hpp"
......@@ -19,16 +20,21 @@ const char* cppa_strip_path(const char* fname);
void cppa_unexpected_message(const char* fname, size_t line_num);
void cppa_unexpected_timeout(const char* fname, size_t line_num);
#define CPPA_FORMAT_STR(stream, fname, line, message) \
stream << cppa_strip_path(fname) << ":" \
<< cppa_fill4(line) << " " << message \
<< std::endl
#define CPPA_STREAMIFY(fname, line, message) \
cppa_strip_path(fname) << ":" << cppa_fill4(line) << " " << message
#define CPPA_PRINT(message) \
CPPA_FORMAT_STR(std::cout, __FILE__, __LINE__, message)
#define CPPA_PRINTC(filename, linenum, message) \
CPPA_LOGF_INFO(CPPA_STREAMIFY(filename, linenum, message)); \
std::cout << CPPA_STREAMIFY(filename, linenum, message) << std::endl
#define CPPA_PRINTERR(message) \
CPPA_FORMAT_STR(std::cerr, __FILE__, __LINE__, message)
#define CPPA_PRINT(message) CPPA_PRINTC(__FILE__, __LINE__, message)
#define CPPA_PRINTERRC(filename, linenum, message) \
CPPA_LOGF_ERROR(CPPA_STREAMIFY(filename, linenum, message)); \
std::cerr << "ERROR: " << CPPA_STREAMIFY(filename, linenum, message) \
<< std::endl
#define CPPA_PRINTERR(message) CPPA_PRINTERRC(__FILE__, __LINE__, message)
template<typename T1, typename T2>
struct both_integral {
......@@ -53,7 +59,7 @@ inline std::string cppa_stream_arg(const bool& value) {
}
inline void cppa_passed(const char* fname, int line_number) {
CPPA_FORMAT_STR(std::cout, fname, line_number, "passed");
CPPA_PRINTC(fname, line_number, "passed");
}
template<typename V1, typename V2>
......@@ -61,11 +67,9 @@ inline void cppa_failed(const V1& v1,
const V2& v2,
const char* fname,
int line_number) {
CPPA_FORMAT_STR(std::cout, fname, line_number,
"ERROR: expected value: "
<< cppa_stream_arg(v2)
<< ", found: "
<< cppa_stream_arg(v1));
CPPA_PRINTERRC(fname, line_number,
"expected value: " << cppa_stream_arg(v2)
<< ", found: " << cppa_stream_arg(v1));
cppa_inc_error_count();
}
......
......@@ -62,7 +62,6 @@ struct replier : public event_based_actor {
// receive seven reply messages (2 local, 5 remote)
void spawn5_server(actor_ptr client, bool inverted) {
auto default_case = others() >> CPPA_UNEXPECTED_MSG_CB();
group_ptr grp;
if (!inverted) {
grp = group::get("local", "foobar");
......@@ -77,43 +76,42 @@ void spawn5_server(actor_ptr client, bool inverted) {
}
spawn_in_group<reflector>(grp);
spawn_in_group<reflector>(grp);
receive_response (sync_send(client, atom("Spawn5"), grp)) (
sync_send(client, atom("Spawn5"), grp).await(
on(atom("ok"), arg_match) >> [&](const actor_vector& vec) {
send(grp, "Hello reflectors!", 5.0);
if (vec.size() != 5) {
CPPA_PRINT("remote client did not spawn five reflectors!");
}
for (auto& a : vec) {
self->monitor(a);
CPPA_PRINTERR("remote client did not spawn five reflectors!");
}
for (auto& a : vec) self->monitor(a);
},
default_case,
after(chrono::seconds(10)) >> [&] {
throw runtime_error("timeout");
}
others() >> CPPA_UNEXPECTED_MSG_CB(),
after(chrono::seconds(10)) >> CPPA_UNEXPECTED_TOUT_CB()
);
CPPA_PRINT("wait for reflected messages");
// receive seven reply messages (2 local, 5 remote)
int x = 0;
receive_for(x, 7) (
on("Hello reflectors!", 5.0) >> [] { }
on("Hello reflectors!", 5.0) >> [] { },
after(std::chrono::seconds(2)) >> CPPA_UNEXPECTED_TOUT_CB()
);
CPPA_PRINT("wait for DOWN messages");
// wait for DOWN messages
{int i = 0; receive_for(i, 5) (
on(atom("DOWN"), arg_match) >> [](std::uint32_t reason) {
if (reason != exit_reason::normal) {
CPPA_PRINT("reflector exited for non-normal exit reason!");
CPPA_PRINTERR("reflector exited for non-normal exit reason!");
}
},
default_case,
others() >> CPPA_UNEXPECTED_MSG_CB(),
after(chrono::seconds(2)) >> [&] {
i = 4;
CPPA_PRINTERR("timeout while waiting for DOWN messages!");
CPPA_UNEXPECTED_TOUT();
}
);}
CPPA_CHECKPOINT();
// wait for locally spawned reflectors
await_all_others_done();
CPPA_CHECKPOINT();
send(client, atom("Spawn5Done"));
}
......@@ -161,7 +159,7 @@ int client_part(const vector<string_pair>& args) {
}
);
await_all_others_done();
receive_response (sync_send(server, atom("SyncMsg"))) (
sync_send(server, atom("SyncMsg")).await(
others() >> [&] {
if (self->last_dequeued() != make_cow_tuple(atom("SyncReply"))) {
ostringstream oss;
......@@ -189,10 +187,8 @@ int client_part(const vector<string_pair>& args) {
);
// test 100 sync_messages
for (int i = 0; i < 100; ++i) {
receive_response (sync_send(server, atom("foo"), atom("bar"), i)) (
on(atom("foo"), atom("bar"), i) >> [] {
},
sync_send(server, atom("foo"), atom("bar"), i).await(
on(atom("foo"), atom("bar"), i) >> [] { },
others() >> [&] {
CPPA_ERROR("unexpected message; "
<< __FILE__ << " line " << __LINE__ << ": "
......@@ -203,20 +199,28 @@ int client_part(const vector<string_pair>& args) {
}
);
}
CPPA_CHECKPOINT();
spawn5_server(server, false);
CPPA_CHECKPOINT();
spawn5_client();
CPPA_CHECKPOINT();
// wait for locally spawned reflectors
await_all_others_done();
CPPA_CHECKPOINT();
receive (
on(atom("fwd"), arg_match) >> [&](const actor_ptr& fwd, const string&) {
forward_to(fwd);
}
);
CPPA_CHECKPOINT();
// shutdown handshake
send(server, atom("farewell"));
CPPA_CHECKPOINT();
receive(on(atom("cu")) >> [] { });
CPPA_CHECKPOINT();
shutdown();
CPPA_CHECKPOINT();
return CPPA_TEST_RESULT();
}
......@@ -251,7 +255,6 @@ int main(int argc, char** argv) {
}
}
CPPA_TEST(test__remote_actor);
CPPA_LOGF_TRACE("");
//auto ping_actor = spawn(ping, 10);
uint16_t port = 4242;
bool success = false;
......@@ -270,11 +273,11 @@ int main(int argc, char** argv) {
thread child;
ostringstream oss;
if (run_remote_actor) {
oss << app_path << " run=remote_actor port=" << port;// << " &>client.txt";
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_LOGF_TRACE("lambda expression calling system()");
CPPA_LOGC_TRACE("NONE", "main$thread_launcher", "");
string cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) {
CPPA_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!");
......@@ -282,9 +285,7 @@ int main(int argc, char** argv) {
}
});
}
else {
CPPA_PRINT("actor published at port " << port);
}
else { CPPA_PRINT("actor published at port " << port); }
//CPPA_PRINT("await SpawnPing message");
actor_ptr remote_client;
CPPA_LOGF_DEBUG("send 'SpawnPing', expect 'PingPtr'");
......
......@@ -302,12 +302,19 @@ int main() {
);
CPPA_CHECKPOINT();
auto mirror = spawn<simple_mirror>();
auto mirror = spawn<simple_mirror,monitored>();
CPPA_PRINT("test mirror");
send(mirror, "hello mirror");
receive(on("hello mirror") >> []() { });
receive (
on("hello mirror") >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
quit_actor(mirror, exit_reason::user_defined);
receive (
on(atom("DOWN"), exit_reason::user_defined) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB()
);
await_all_others_done();
CPPA_CHECKPOINT();
......@@ -620,19 +627,21 @@ int main() {
CPPA_CHECK_EQUAL("wait4int", res1);
CPPA_CHECK_EQUAL(behavior_test<event_testee>(spawn<event_testee>()), "wait4int");
// create 20,000 actors linked to one single actor
// create some actors linked to one single actor
// and kill them all through killing the link
auto twenty_thousand = spawn([] {
for (int i = 0; i < 20000; ++i) {
spawn<event_testee, linked>();
auto legion = spawn([] {
CPPA_LOGF_INFO("spawn 1,000 actors");
for (int i = 0; i < 1000; ++i) {
spawn<event_testee,linked>();
}
become(others() >> CPPA_UNEXPECTED_MSG_CB());
});
quit_actor(twenty_thousand, exit_reason::user_defined);
quit_actor(legion, exit_reason::user_defined);
await_all_others_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 = spawn<monitored+blocking_api>(ping, 10);
auto pong_actor = spawn<monitored+blocking_api>(pong, ping_actor);
self->link_to(pong_actor);
int i = 0;
int flags = 0;
......@@ -648,11 +657,11 @@ int main() {
auto who = self->last_sender();
if (who == pong_actor) {
flags |= 0x02;
CPPA_CHECK_EQUAL(exit_reason::user_defined, reason);
CPPA_CHECK_EQUAL(reason, exit_reason::user_defined);
}
else if (who == ping_actor) {
flags |= 0x04;
CPPA_CHECK_EQUAL(exit_reason::normal, reason);
CPPA_CHECK_EQUAL(reason, exit_reason::normal);
}
},
on_arg_match >> [&](const atom_value& val) {
......
......@@ -109,14 +109,13 @@ struct C : sb_actor<C> {
\******************************************************************************/
struct D : popular_actor {
response_handle m_handle;
D(const actor_ptr& buddy) : popular_actor(buddy) { }
void init() {
become (
others() >> [=] {
m_handle = make_response_handle();
response_handle handle = make_response_handle();
sync_send_tuple(buddy(), last_dequeued()).then([=] {
m_handle.apply(last_dequeued());
reply_to(handle, last_dequeued());
quit();
});
}
......@@ -171,7 +170,8 @@ int main() {
self->on_sync_failure([] {
CPPA_ERROR("received: " << to_string(self->last_dequeued()));
});
spawn<monitored + blocking_api>([&] {
spawn<monitored + blocking_api>([] {
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"));
......@@ -276,9 +276,9 @@ int main() {
CPPA_CHECKPOINT();
// test use case 3
spawn<monitored>([] { // client
auto s = spawn<server, linked>(); // server
auto w = spawn<linked>([] { // worker
spawn<monitored + blocking_api>([] { // client
auto s = spawn<server, linked>(); // server
auto w = spawn<linked>([] { // worker
become(on(atom("request")) >> []{ reply(atom("response")); });
});
// first 'idle', then 'request'
......
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