Commit ea2a3771 authored by Joseph Noir's avatar Joseph Noir

Merge branch 'unstable' into topic/opencl

Conflicts:
	src/singleton_manager.cpp
parents 4c62e717 ef0b744f
...@@ -85,11 +85,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "") ...@@ -85,11 +85,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
endif ("${CMAKE_BUILD_TYPE}" STREQUAL "") endif ("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(LIBCPPA_SRC set(LIBCPPA_SRC
src/abstract_scheduled_actor.cpp
src/abstract_tuple.cpp src/abstract_tuple.cpp
src/actor.cpp src/actor.cpp
src/actor_addressing.cpp src/actor_addressing.cpp
src/actor_count.cpp
src/actor_proxy.cpp src/actor_proxy.cpp
src/actor_registry.cpp src/actor_registry.cpp
src/any_tuple.cpp src/any_tuple.cpp
......
...@@ -18,10 +18,7 @@ cppa/cow_tuple.hpp ...@@ -18,10 +18,7 @@ cppa/cow_tuple.hpp
cppa/cppa.hpp cppa/cppa.hpp
cppa/cppa_fwd.hpp cppa/cppa_fwd.hpp
cppa/deserializer.hpp cppa/deserializer.hpp
cppa/detail/abstract_actor.hpp
cppa/detail/abstract_scheduled_actor.hpp
cppa/detail/abstract_tuple.hpp cppa/detail/abstract_tuple.hpp
cppa/detail/actor_count.hpp
cppa/detail/actor_registry.hpp cppa/detail/actor_registry.hpp
cppa/detail/atom_val.hpp cppa/detail/atom_val.hpp
cppa/detail/behavior_impl.hpp cppa/detail/behavior_impl.hpp
...@@ -56,7 +53,7 @@ cppa/detail/scheduled_actor_dummy.hpp ...@@ -56,7 +53,7 @@ cppa/detail/scheduled_actor_dummy.hpp
cppa/detail/serialize_tuple.hpp cppa/detail/serialize_tuple.hpp
cppa/detail/singleton_manager.hpp cppa/detail/singleton_manager.hpp
cppa/detail/singleton_mixin.hpp cppa/detail/singleton_mixin.hpp
cppa/detail/stacked_actor_mixin.hpp cppa/stacked.hpp
cppa/detail/swap_bytes.hpp cppa/detail/swap_bytes.hpp
cppa/detail/tdata.hpp cppa/detail/tdata.hpp
cppa/detail/thread_pool_scheduler.hpp cppa/detail/thread_pool_scheduler.hpp
...@@ -72,7 +69,7 @@ cppa/detail/uniform_type_info_map.hpp ...@@ -72,7 +69,7 @@ cppa/detail/uniform_type_info_map.hpp
cppa/detail/value_guard.hpp cppa/detail/value_guard.hpp
cppa/detail/yield_interface.hpp cppa/detail/yield_interface.hpp
cppa/either.hpp cppa/either.hpp
cppa/enable_weak_ptr_mixin.hpp cppa/enable_weak_ptr.hpp
cppa/event_based_actor.hpp cppa/event_based_actor.hpp
cppa/exception.hpp cppa/exception.hpp
cppa/exit_reason.hpp cppa/exit_reason.hpp
...@@ -125,7 +122,6 @@ cppa/response_handle.hpp ...@@ -125,7 +122,6 @@ cppa/response_handle.hpp
cppa/sb_actor.hpp cppa/sb_actor.hpp
cppa/scheduled_actor.hpp cppa/scheduled_actor.hpp
cppa/scheduler.hpp cppa/scheduler.hpp
cppa/scheduling_hint.hpp
cppa/self.hpp cppa/self.hpp
cppa/serializer.hpp cppa/serializer.hpp
cppa/thread_mapped_actor.hpp cppa/thread_mapped_actor.hpp
...@@ -198,11 +194,9 @@ examples/type_system/announce_2.cpp ...@@ -198,11 +194,9 @@ examples/type_system/announce_2.cpp
examples/type_system/announce_3.cpp examples/type_system/announce_3.cpp
examples/type_system/announce_4.cpp examples/type_system/announce_4.cpp
examples/type_system/announce_5.cpp examples/type_system/announce_5.cpp
src/abstract_scheduled_actor.cpp
src/abstract_tuple.cpp src/abstract_tuple.cpp
src/actor.cpp src/actor.cpp
src/actor_addressing.cpp src/actor_addressing.cpp
src/actor_count.cpp
src/actor_proxy.cpp src/actor_proxy.cpp
src/actor_registry.cpp src/actor_registry.cpp
src/any_tuple.cpp src/any_tuple.cpp
...@@ -289,7 +283,6 @@ unit_testing/test_tuple.cpp ...@@ -289,7 +283,6 @@ unit_testing/test_tuple.cpp
unit_testing/test_metaprogramming.cpp unit_testing/test_metaprogramming.cpp
unit_testing/test_uniform_type.cpp unit_testing/test_uniform_type.cpp
unit_testing/test_yield_interface.cpp unit_testing/test_yield_interface.cpp
cppa/memory_cached_mixin.hpp
unit_testing/test.cpp unit_testing/test.cpp
cppa/opencl/program.hpp cppa/opencl/program.hpp
cppa/opencl/actor_facade.hpp cppa/opencl/actor_facade.hpp
...@@ -304,3 +297,8 @@ src/opencl/actor_facade.cpp ...@@ -304,3 +297,8 @@ src/opencl/actor_facade.cpp
cppa/opencl/smart_ptr.hpp cppa/opencl/smart_ptr.hpp
src/exit_reason.cpp src/exit_reason.cpp
src/on.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,19 +31,27 @@ ...@@ -31,19 +31,27 @@
#ifndef CPPA_ACTOR_HPP #ifndef CPPA_ACTOR_HPP
#define CPPA_ACTOR_HPP #define CPPA_ACTOR_HPP
#include <mutex>
#include <atomic>
#include <memory> #include <memory>
#include <vector>
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
#include "cppa/group.hpp" #include "cppa/group.hpp"
#include "cppa/channel.hpp" #include "cppa/channel.hpp"
#include "cppa/cppa_fwd.hpp"
#include "cppa/attachable.hpp" #include "cppa/attachable.hpp"
#include "cppa/message_id.hpp" #include "cppa/message_id.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/intrusive_ptr.hpp"
#include "cppa/util/rm_ref.hpp" #include "cppa/util/rm_ref.hpp"
namespace cppa { namespace cppa {
class actor;
class self_type;
class serializer; class serializer;
class deserializer; class deserializer;
...@@ -53,6 +61,12 @@ class deserializer; ...@@ -53,6 +61,12 @@ class deserializer;
*/ */
typedef std::uint32_t actor_id; 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. * @brief Base class for all actor implementations.
*/ */
...@@ -65,13 +79,15 @@ class actor : public channel { ...@@ -65,13 +79,15 @@ class actor : public channel {
* this actor is an scheduled actor that successfully changed * this actor is an scheduled actor that successfully changed
* its state to @p pending in response to the enqueue operation. * 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. * @brief Enqueues @p msg as a synchronous message to this actor's mailbox.
* @pre <tt>id.valid()</tt> * @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 * @brief Enqueues @p msg as a reply to @p request_id
...@@ -79,7 +95,7 @@ class actor : public channel { ...@@ -79,7 +95,7 @@ class actor : public channel {
* this actor is an scheduled actor that successfully changed * this actor is an scheduled actor that successfully changed
* its state to @p pending in response to the enqueue operation. * 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, message_id_t id,
any_tuple msg); any_tuple msg);
...@@ -94,46 +110,40 @@ class actor : public channel { ...@@ -94,46 +110,40 @@ class actor : public channel {
* otherwise (actor already exited) @p false. * otherwise (actor already exited) @p false.
* @warning The actor takes ownership of @p ptr. * @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 * @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. * 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. * 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. * otherwise (actor already exited) @p false.
*/ */
template<typename F> template<typename F>
bool attach_functor(F&& ftor); bool attach_functor(F&& f);
/** /**
* @brief Detaches the first attached object that matches @p what. * @brief Detaches the first attached object that matches @p what.
*/ */
virtual void detach(const attachable::token& what) = 0; virtual void detach(const attachable::token& what);
template<typename T>
bool attach(std::unique_ptr<T>&& ptr,
typename std::enable_if<
std::is_base_of<attachable,T>::value
>::type* = 0);
/** /**
* @brief Links this actor to @p other. * @brief Links this actor to @p other.
* @param other Actor instance that whose execution is coupled to the * @param other Actor instance that whose execution is coupled to the
* execution of this Actor. * 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. * @brief Unlinks this actor from @p other.
* @param other Linked Actor. * @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. * @brief Establishes a link relation between this actor and @p other.
...@@ -141,7 +151,7 @@ class actor : public channel { ...@@ -141,7 +151,7 @@ class actor : public channel {
* @returns @c true if this actor is running and added @p other to its * @returns @c true if this actor is running and added @p other to its
* list of linked actors; otherwise @c false. * 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. * @brief Removes a link relation between this actor and @p other.
...@@ -149,7 +159,7 @@ class actor : public channel { ...@@ -149,7 +159,7 @@ class actor : public channel {
* @returns @c true if this actor is running and removed @p other * @returns @c true if this actor is running and removed @p other
* from its list of linked actors; otherwise @c false. * 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 * @brief Gets an integer value that uniquely identifies this Actor in
...@@ -172,23 +182,59 @@ class actor : public channel { ...@@ -172,23 +182,59 @@ class actor : public channel {
actor(actor_id aid); actor(actor_id aid);
private: /**
* @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);
actor_id m_id; /**
bool m_is_proxy; * @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 A smart pointer type that manages instances of {@link actor}. * @brief Returns the actor's exit reason of
* @relates actor * <tt>exit_reason::not_exited</tt> if it's still alive.
*/ */
typedef intrusive_ptr<actor> actor_ptr; inline std::uint32_t exit_reason() const;
class self_type; /**
* @brief Returns <tt>exit_reason() != exit_reason::not_exited</tt>.
*/
inline bool exited() const;
private:
// cannot be changed after construction
const actor_id m_id;
// you're either a proxy or you're not
const bool m_is_proxy;
// initially exit_reason::not_exited
std::atomic<std::uint32_t> m_exit_reason;
bool operator==(const actor_ptr& lhs, const self_type& rhs); // guards access to m_exit_reason, m_attachables, and m_links
bool operator!=(const self_type& lhs, const actor_ptr& rhs); 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;
};
// 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 * * inline and template member function implementations *
...@@ -202,39 +248,33 @@ inline bool actor::is_proxy() const { ...@@ -202,39 +248,33 @@ inline bool actor::is_proxy() const {
return m_is_proxy; return m_is_proxy;
} }
template<typename T> inline std::uint32_t actor::exit_reason() const {
bool actor::attach(std::unique_ptr<T>&& ptr, return m_exit_reason;
typename std::enable_if< }
std::is_base_of<attachable,T>::value
>::type*) { inline bool actor::exited() const {
return attach(static_cast<attachable*>(ptr.release())); return exit_reason() != exit_reason::not_exited;
} }
template<class F> template<class F>
class functor_attachable : public attachable { struct functor_attachable : attachable {
F m_functor; F m_functor;
public: template<typename T>
inline functor_attachable(T&& arg) : m_functor(std::forward<T>(arg)) { }
template<class FArg>
functor_attachable(FArg&& arg) : m_functor(std::forward<FArg>(arg)) {
}
void actor_exited(std::uint32_t reason) { void actor_exited(std::uint32_t reason) { m_functor(reason); }
m_functor(reason);
}
bool matches(const attachable::token&) { bool matches(const attachable::token&) { return false; }
return false;
}
}; };
template<typename F> template<typename F>
bool actor::attach_functor(F&& ftor) { bool actor::attach_functor(F&& f) {
typedef typename util::rm_ref<F>::type f_type; 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 } // namespace cppa
......
...@@ -36,14 +36,15 @@ ...@@ -36,14 +36,15 @@
#include "cppa/self.hpp" #include "cppa/self.hpp"
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
#include "cppa/extend.hpp"
#include "cppa/match_expr.hpp" #include "cppa/match_expr.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/partial_function.hpp" #include "cppa/partial_function.hpp"
#include "cppa/util/shared_spinlock.hpp" #include "cppa/util/shared_spinlock.hpp"
#include "cppa/util/shared_lock_guard.hpp" #include "cppa/util/shared_lock_guard.hpp"
#include "cppa/detail/memory.hpp" #include "cppa/detail/memory.hpp"
#include "cppa/detail/abstract_actor.hpp"
namespace cppa { namespace cppa {
...@@ -90,7 +91,7 @@ class actor_companion_mixin : public Base { ...@@ -90,7 +91,7 @@ class actor_companion_mixin : public Base {
*/ */
template<typename... Args> template<typename... Args>
void set_message_handler(Args&&... matchExpressions) { 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 { ...@@ -108,7 +109,7 @@ class actor_companion_mixin : public Base {
private: private:
class companion : public detail::abstract_actor<local_actor> { class companion : public local_actor {
friend class actor_companion_mixin; friend class actor_companion_mixin;
typedef util::shared_spinlock lock_type; typedef util::shared_spinlock lock_type;
...@@ -123,24 +124,26 @@ class actor_companion_mixin : public Base { ...@@ -123,24 +124,26 @@ class actor_companion_mixin : public Base {
std::lock_guard<lock_type> guard(m_lock); std::lock_guard<lock_type> guard(m_lock);
m_parent = nullptr; m_parent = nullptr;
} }
cleanup(cppa::exit_reason::normal); cleanup(exit_reason::normal);
} }
void enqueue(cppa::actor* sender, cppa::any_tuple msg) { void enqueue(const actor_ptr& sender, any_tuple msg) {
cppa::util::shared_lock_guard<lock_type> guard(m_lock); util::shared_lock_guard<lock_type> guard(m_lock);
if (m_parent) { if (m_parent) {
m_parent->new_message(new_node_ptr(sender, std::move(msg))); 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) { void sync_enqueue(const actor_ptr& sender,
cppa::util::shared_lock_guard<lock_type> guard(m_lock); message_id_t id,
any_tuple msg) {
util::shared_lock_guard<lock_type> guard(m_lock);
if (m_parent) { if (m_parent) {
m_parent->new_message(new_node_ptr(sender, std::move(msg), id)); 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) { void quit(std::uint32_t) {
throw std::runtime_error("ActorWidgetMixin::Gateway::exit " throw std::runtime_error("ActorWidgetMixin::Gateway::exit "
...@@ -148,21 +151,21 @@ class actor_companion_mixin : public Base { ...@@ -148,21 +151,21 @@ class actor_companion_mixin : public Base {
"management instead!"); "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(); throw_no_recv();
} }
void become_waiting_for(cppa::behavior&&, cppa::message_id_t) { void become_waiting_for(behavior&&, message_id_t) {
throw_no_become(); throw_no_become();
} }
protected: protected:
void do_become(cppa::behavior&&, bool) { throw_no_become(); } void do_become(behavior&&, bool) { throw_no_become(); }
private: private:
...@@ -172,13 +175,13 @@ class actor_companion_mixin : public Base { ...@@ -172,13 +175,13 @@ class actor_companion_mixin : public Base {
} }
void throw_no_become() { void throw_no_become() {
throw std::runtime_error("actor_companion_mixin::companion " throw std::runtime_error("actor_companion_companion "
"does not support libcppa's " "does not support libcppa's "
"become() API"); "become() API");
} }
void throw_no_recv() { void throw_no_recv() {
throw std::runtime_error("actor_companion_mixin::companion " throw std::runtime_error("actor_companion_companion "
"does not support libcppa's " "does not support libcppa's "
"receive() API"); "receive() API");
} }
......
...@@ -32,8 +32,9 @@ ...@@ -32,8 +32,9 @@
#define CPPA_ACTOR_PROXY_HPP #define CPPA_ACTOR_PROXY_HPP
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
#include "cppa/extend.hpp"
#include "cppa/weak_intrusive_ptr.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" #include "cppa/network/message_header.hpp"
...@@ -43,10 +44,11 @@ class actor_proxy_cache; ...@@ -43,10 +44,11 @@ class actor_proxy_cache;
/** /**
* @brief Represents a remote actor. * @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: public:
......
...@@ -28,21 +28,20 @@ ...@@ -28,21 +28,20 @@
\******************************************************************************/ \******************************************************************************/
#ifndef CPPA_ACTOR_COUNT_HPP #ifndef CPPA_ACTOR_STATE_HPP
#define CPPA_ACTOR_COUNT_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(); } // namespace cppa
void dec_actor_count();
/* #endif // CPPA_ACTOR_STATE_HPP
* @pre @p expected <= 1
*/
void actor_count_wait_until(size_t expected);
} } // namespace cppa::detail
#endif // CPPA_ACTOR_COUNT_HPP
...@@ -53,7 +53,7 @@ namespace cppa { ...@@ -53,7 +53,7 @@ namespace cppa {
* foo(1,2)<br> * foo(1,2)<br>
* foo_pair(3,4) * foo_pair(3,4)
* </tt> * </tt>
* @example announce_example_1.cpp * @example announce_1.cpp
*/ */
/** /**
...@@ -62,7 +62,7 @@ namespace cppa { ...@@ -62,7 +62,7 @@ namespace cppa {
* The output of this example program is: * The output of this example program is:
* *
* <tt>foo(1,2)</tt> * <tt>foo(1,2)</tt>
* @example announce_example_2.cpp * @example announce_2.cpp
*/ */
/** /**
...@@ -72,7 +72,7 @@ namespace cppa { ...@@ -72,7 +72,7 @@ namespace cppa {
* The output of this example program is: * The output of this example program is:
* *
* <tt>foo(1,2)</tt> * <tt>foo(1,2)</tt>
* @example announce_example_3.cpp * @example announce_3.cpp
*/ */
/** /**
...@@ -81,13 +81,13 @@ namespace cppa { ...@@ -81,13 +81,13 @@ namespace cppa {
* The output of this example program is: * The output of this example program is:
* *
* <tt>bar(foo(1,2),3)</tt> * <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 * @brief An advanced example for @c announce implementing serialization
* for a user-defined tree data type. * 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); ...@@ -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. * @brief Creates meta informations for a non-trivial member @p C.
* @param c_ptr Pointer to the non-trivial member. * @param c_ptr Pointer to the non-trivial member.
* @param args "Sub-members" of @p c_ptr * @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. * @returns A pair of @p c_ptr and the created meta informations.
*/ */
template<class C, class Parent, typename... Args> template<class C, class Parent, typename... Args>
...@@ -120,7 +120,7 @@ compound_member(C Parent::*c_ptr, const Args&... args) { ...@@ -120,7 +120,7 @@ compound_member(C Parent::*c_ptr, const Args&... args) {
* via a getter returning a mutable reference. * via a getter returning a mutable reference.
* @param getter Member function pointer to the getter. * @param getter Member function pointer to the getter.
* @param args "Sub-members" of @p c_ptr * @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. * @returns A pair of @p c_ptr and the created meta informations.
*/ */
template<class C, class Parent, typename... Args> template<class C, class Parent, typename... Args>
...@@ -136,7 +136,7 @@ compound_member(C& (Parent::*getter)(), const Args&... args) { ...@@ -136,7 +136,7 @@ compound_member(C& (Parent::*getter)(), const Args&... args) {
* @param gspair A pair of two member function pointers representing * @param gspair A pair of two member function pointers representing
* getter and setter. * getter and setter.
* @param args "Sub-members" of @p c_ptr * @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. * @returns A pair of @p c_ptr and the created meta informations.
*/ */
template<class Parent, typename GRes, template<class Parent, typename GRes,
......
...@@ -56,7 +56,14 @@ class any_tuple { ...@@ -56,7 +56,14 @@ class any_tuple {
public: 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; typedef detail::abstract_tuple::const_iterator const_iterator;
/** /**
...@@ -125,131 +132,97 @@ class any_tuple { ...@@ -125,131 +132,97 @@ class any_tuple {
/** /**
* @brief Returns true if <tt>size() == 0</tt>, otherwise false. * @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> template<typename T>
inline const T& get_as(size_t p) const { inline const T& get_as(size_t p) const;
CPPA_REQUIRE(*(type_at(p)) == typeid(T));
return *reinterpret_cast<const T*>(at(p));
}
/**
* @brief Returns the value at @p as mutable data_ptr& of type @p T&.
*/
template<typename T> template<typename T>
inline T& get_as_mutable(size_t p) { inline T& get_as_mutable(size_t p);
CPPA_REQUIRE(*(type_at(p)) == typeid(T));
return *reinterpret_cast<T*>(mutable_at(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;
inline value_ptr& vals() { return m_vals; } /**
inline const value_ptr& vals() const { return m_vals; } * @brief Returns a copy-on-write pointer to the internal data.
inline const value_ptr& cvals() const { return m_vals; } */
inline data_ptr& vals();
inline const std::type_info* type_token() const { /**
return m_vals->type_token(); * @brief Returns a const copy-on-write pointer to the internal data.
} */
inline const data_ptr& vals() const;
inline detail::tuple_impl_info impl_type() const { /**
return m_vals->impl_type(); * @brief Returns a const copy-on-write pointer to the internal data.
} */
inline const data_ptr& cvals() const;
template<typename T> /**
static inline any_tuple view(T&& value, * @brief Returns either <tt>&typeid(util::type_list<Ts...>)</tt>, where
typename std::enable_if< * <tt>Ts...</tt> are the element types, or <tt>&typeid(void)</tt>.
util::is_iterable< *
typename util::rm_ref<T>::type * The type token @p &typeid(void) indicates that this tuple is dynamically
>::value * typed, i.e., the types where not available at compile time.
>::type* = 0) { */
static constexpr bool can_optimize = std::is_reference<T>::value inline const std::type_info* type_token() const;
&& !std::is_const<T>::value;
std::integral_constant<bool, can_optimize> token; /**
return any_tuple{container_view(std::forward<T>(value), token)}; * @brief Checks whether this
} */
inline bool dynamically_typed() const;
template<typename T> template<typename T>
static inline any_tuple view(T&& value, 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(); void reset();
explicit any_tuple(detail::abstract_tuple*); 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: private:
value_ptr m_vals; data_ptr m_vals;
explicit any_tuple(const value_ptr& vals); explicit any_tuple(const data_ptr& vals);
typedef detail::abstract_tuple* abstract_ptr; template<typename T, typename CanOptimize>
static any_tuple view(T&& value, std::true_type, CanOptimize token);
template<typename T> template<typename T, typename CanOptimize>
static inline abstract_ptr simple_view(T& value, std::true_type) { static any_tuple view(T&& value, std::false_type, CanOptimize token);
return new detail::tuple_view<T>(&value);
}
template<typename First, typename Second> template<typename T, typename U>
static inline abstract_ptr simple_view(std::pair<First, Second>& p, static any_tuple view(std::pair<T,U> p, std::false_type);
std::true_type) {
return new detail::tuple_view<First, Second>(&p.first, &p.second);
}
template<typename T> template<typename T>
static inline abstract_ptr simple_view(T&& value, std::false_type) { static detail::abstract_tuple* simple_view(T& value, std::true_type);
typedef typename util::rm_ref<T>::type vtype;
typedef typename detail::implicit_conversions<vtype>::type converted; template<typename T, typename U>
return new detail::tuple_vals<converted>(std::forward<T>(value)); static detail::abstract_tuple* simple_view(std::pair<T,U>& p, std::true_type);
}
template<typename First, typename Second> template<typename T>
static inline any_tuple view(std::pair<First, Second> p, std::false_type) { static detail::abstract_tuple* simple_view(T&& value, std::false_type);
return new detail::tuple_vals<First, Second>(std::move(p.first),
std::move(p.second));
}
template<typename T> template<typename T>
static inline abstract_ptr container_view(T& value, std::true_type) { static detail::abstract_tuple* container_view(T& value, std::true_type);
return new detail::container_tuple_view<T>(&value);
}
template<typename T> template<typename T>
static inline abstract_ptr container_view(T&& value, std::false_type) { static detail::abstract_tuple* 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);
}
}; };
...@@ -276,6 +249,138 @@ inline any_tuple make_any_tuple(Args&&... args) { ...@@ -276,6 +249,138 @@ inline any_tuple make_any_tuple(Args&&... args) {
return make_cow_tuple(std::forward<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 } // namespace cppa
#endif // CPPA_ANY_TUPLE_HPP #endif // CPPA_ANY_TUPLE_HPP
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#ifndef CPPA_ATTACHABLE_HPP #ifndef CPPA_ATTACHABLE_HPP
#define CPPA_ATTACHABLE_HPP #define CPPA_ATTACHABLE_HPP
#include <memory>
#include <cstdint> #include <cstdint>
#include <typeinfo> #include <typeinfo>
...@@ -54,17 +55,19 @@ class attachable { ...@@ -54,17 +55,19 @@ class attachable {
* @brief Represents a pointer to a value with its RTTI. * @brief Represents a pointer to a value with its RTTI.
*/ */
struct token { struct token {
/** /**
* @brief Denotes the type of @c ptr. * @brief Denotes the type of @c ptr.
*/ */
const std::type_info& subtype; const std::type_info& subtype;
/** /**
* @brief Any value, used to identify @c attachable instances. * @brief Any value, used to identify @c attachable instances.
*/ */
const void* ptr; 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(); virtual ~attachable();
...@@ -86,6 +89,12 @@ class attachable { ...@@ -86,6 +89,12 @@ class attachable {
}; };
/**
* @brief A managed {@link attachable} pointer.
* @relates attachable
*/
typedef std::unique_ptr<attachable> attachable_ptr;
} // namespace cppa } // namespace cppa
#endif // CPPA_ATTACHABLE_HPP #endif // CPPA_ATTACHABLE_HPP
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#include "cppa/util/if_else.hpp" #include "cppa/util/if_else.hpp"
#include "cppa/util/duration.hpp" #include "cppa/util/duration.hpp"
#include "cppa/util/type_list.hpp" #include "cppa/util/type_list.hpp"
#include "cppa/util/disjunction.hpp"
namespace cppa { namespace cppa {
...@@ -59,6 +58,8 @@ class behavior { ...@@ -59,6 +58,8 @@ class behavior {
typedef intrusive_ptr<detail::behavior_impl> impl_ptr; typedef intrusive_ptr<detail::behavior_impl> impl_ptr;
static constexpr bool may_have_timeout = true;
behavior() = default; behavior() = default;
behavior(behavior&&) = default; behavior(behavior&&) = default;
behavior(const behavior&) = default; behavior(const behavior&) = default;
...@@ -85,7 +86,7 @@ class behavior { ...@@ -85,7 +86,7 @@ class behavior {
template<typename... Cases, typename Arg1, typename... Args> template<typename... Cases, typename Arg1, typename... Args>
behavior(const match_expr<Cases...>& arg0, const Arg1& arg1, const Args&... 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() { inline void handle_timeout() {
m_impl->handle_timeout(); m_impl->handle_timeout();
...@@ -109,6 +110,8 @@ class behavior { ...@@ -109,6 +110,8 @@ class behavior {
return {new detail::continuation_decorator<F>(std::move(fun), m_impl)}; return {new detail::continuation_decorator<F>(std::move(fun), m_impl)};
} }
const impl_ptr& as_behavior_impl() const { return m_impl; }
private: private:
impl_ptr m_impl; impl_ptr m_impl;
...@@ -121,7 +124,6 @@ inline behavior operator,(const match_expr<Lhs...>& lhs, ...@@ -121,7 +124,6 @@ inline behavior operator,(const match_expr<Lhs...>& lhs,
return match_expr_convert(lhs, rhs); return match_expr_convert(lhs, rhs);
} }
} // namespace cppa } // namespace cppa
#endif // CPPA_BEHAVIOR_HPP #endif // CPPA_BEHAVIOR_HPP
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#ifndef CPPA_CHANNEL_HPP #ifndef CPPA_CHANNEL_HPP
#define CPPA_CHANNEL_HPP #define CPPA_CHANNEL_HPP
#include <type_traits>
#include "cppa/ref_counted.hpp" #include "cppa/ref_counted.hpp"
#include "cppa/intrusive_ptr.hpp" #include "cppa/intrusive_ptr.hpp"
...@@ -41,6 +43,8 @@ class actor; ...@@ -41,6 +43,8 @@ class actor;
class group; class group;
class any_tuple; class any_tuple;
typedef intrusive_ptr<actor> actor_ptr;
/** /**
* @brief Interface for all message receivers. * @brief Interface for all message receivers.
* *
...@@ -57,7 +61,7 @@ class channel : public ref_counted { ...@@ -57,7 +61,7 @@ class channel : public ref_counted {
/** /**
* @brief Enqueues @p msg to the list of received messages. * @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: protected:
...@@ -77,6 +81,12 @@ class channel : public ref_counted { ...@@ -77,6 +81,12 @@ class channel : public ref_counted {
*/ */
typedef intrusive_ptr<channel> channel_ptr; 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 } // namespace cppa
#endif // CPPA_CHANNEL_HPP #endif // CPPA_CHANNEL_HPP
...@@ -35,49 +35,33 @@ ...@@ -35,49 +35,33 @@
#include "cppa/config.hpp" #include "cppa/config.hpp"
#include "cppa/either.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/receive_policy.hpp"
#include "cppa/detail/behavior_stack.hpp" #include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/yield_interface.hpp" #include "cppa/detail/yield_interface.hpp"
#include "cppa/detail/stacked_actor_mixin.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa { namespace cppa {
#ifdef CPPA_DOCUMENTATION
/** /**
* @brief Context-switching actor implementation. * @brief Context-switching actor implementation.
* @extends scheduled_actor
*/ */
class context_switching_actor : public scheduled_actor { class context_switching_actor : public extend<scheduled_actor,context_switching_actor>::with<stacked> {
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> {
friend class detail::behavior_stack; friend class detail::behavior_stack;
friend class detail::receive_policy; friend class detail::receive_policy;
typedef detail::stacked_actor_mixin< typedef combined_type super;
context_switching_actor,
detail::abstract_scheduled_actor> super;
public: public:
/**
* @brief Creates a context-switching actor running @p fun.
*/
context_switching_actor(std::function<void()> fun); context_switching_actor(std::function<void()> fun);
resume_result resume(util::fiber* from, actor_ptr& next_job); //override resume_result resume(util::fiber* from, actor_ptr& next_job); //override
...@@ -86,25 +70,15 @@ class context_switching_actor : public detail::stacked_actor_mixin< ...@@ -86,25 +70,15 @@ class context_switching_actor : public detail::stacked_actor_mixin<
protected: 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: private:
// required by detail::nestable_receive_policy
static const detail::receive_policy_flag receive_flag = detail::rp_nestable;
detail::recursive_queue_node* receive_node(); 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 // required by util::fiber
static void trampoline(void* _this); static void trampoline(void* _this);
...@@ -114,8 +88,6 @@ class context_switching_actor : public detail::stacked_actor_mixin< ...@@ -114,8 +88,6 @@ class context_switching_actor : public detail::stacked_actor_mixin<
}; };
#endif // CPPA_DOCUMENTATION
} // namespace cppa } // namespace cppa
#endif // CPPA_CONTEXT_SWITCHING_ACTOR_HPP #endif // CPPA_CONTEXT_SWITCHING_ACTOR_HPP
...@@ -170,8 +170,6 @@ struct cow_tuple_from_type_list< util::type_list<Ts...> > { ...@@ -170,8 +170,6 @@ struct cow_tuple_from_type_list< util::type_list<Ts...> > {
typedef cow_tuple<Ts...> type; typedef cow_tuple<Ts...> type;
}; };
#ifdef CPPA_DOCUMENTATION
/** /**
* @ingroup CopyOnWrite * @ingroup CopyOnWrite
* @brief Gets a const-reference to the <tt>N</tt>th element of @p tup. * @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...> > { ...@@ -180,8 +178,11 @@ struct cow_tuple_from_type_list< util::type_list<Ts...> > {
* <tt>N</tt>th element of @p tup. * <tt>N</tt>th element of @p tup.
* @relates cow_tuple * @relates cow_tuple
*/ */
template<size_t N, typename T> template<size_t N, typename... Ts>
const T& get(const cow_tuple<...>& tup); 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 * @ingroup CopyOnWrite
...@@ -192,8 +193,11 @@ const T& get(const cow_tuple<...>& tup); ...@@ -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. * @note Detaches @p tup if there are two or more references to the cow_tuple data.
* @relates cow_tuple * @relates cow_tuple
*/ */
template<size_t N, typename T> template<size_t N, typename... Ts>
T& get_ref(cow_tuple<...>& tup); 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 * @ingroup ImplicitConversion
...@@ -202,31 +206,12 @@ T& get_ref(cow_tuple<...>& tup); ...@@ -202,31 +206,12 @@ T& get_ref(cow_tuple<...>& tup);
* @returns A cow_tuple object containing the values @p args. * @returns A cow_tuple object containing the values @p args.
* @relates cow_tuple * @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> template<typename... Args>
cow_tuple<typename detail::strip_and_convert<Args>::type...> cow_tuple<typename detail::strip_and_convert<Args>::type...>
make_cow_tuple(Args&&... args) { make_cow_tuple(Args&&... args) {
return {std::forward<Args>(args)...}; return {std::forward<Args>(args)...};
} }
#endif
/** /**
* @brief Compares two cow_tuples. * @brief Compares two cow_tuples.
* @param lhs First cow_tuple object. * @param lhs First cow_tuple object.
......
...@@ -53,20 +53,21 @@ ...@@ -53,20 +53,21 @@
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/cow_tuple.hpp" #include "cppa/cow_tuple.hpp"
#include "cppa/tuple_cast.hpp" #include "cppa/tuple_cast.hpp"
#include "cppa/singletons.hpp"
#include "cppa/exit_reason.hpp" #include "cppa/exit_reason.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/spawn_options.hpp"
#include "cppa/message_future.hpp" #include "cppa/message_future.hpp"
#include "cppa/response_handle.hpp" #include "cppa/response_handle.hpp"
#include "cppa/scheduled_actor.hpp" #include "cppa/scheduled_actor.hpp"
#include "cppa/scheduling_hint.hpp"
#include "cppa/event_based_actor.hpp" #include "cppa/event_based_actor.hpp"
#include "cppa/util/rm_ref.hpp" #include "cppa/util/rm_ref.hpp"
#include "cppa/network/acceptor.hpp" #include "cppa/network/acceptor.hpp"
#include "cppa/detail/memory.hpp" #include "cppa/detail/memory.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/get_behavior.hpp" #include "cppa/detail/get_behavior.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/receive_loop_helper.hpp" #include "cppa/detail/receive_loop_helper.hpp"
/** /**
...@@ -119,11 +120,11 @@ ...@@ -119,11 +120,11 @@
* *
* @section IntroHelloWorld Hello World Example * @section IntroHelloWorld Hello World Example
* *
* @include hello_world_example.cpp * @include hello_world.cpp
* *
* @section IntroMoreExamples More Examples * @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}. * of {@link receive_loop} and {@link cppa::arg_match arg_match}.
* The {@link dining_philosophers.cpp Dining Philosophers Example} * The {@link dining_philosophers.cpp Dining Philosophers Example}
* introduces event-based actors and includes a lot of <tt>libcppa</tt> * introduces event-based actors and includes a lot of <tt>libcppa</tt>
...@@ -389,13 +390,13 @@ ...@@ -389,13 +390,13 @@
/** /**
* @brief A trivial example program. * @brief A trivial example program.
* @example hello_world_example.cpp * @example hello_world.cpp
*/ */
/** /**
* @brief Shows the usage of {@link cppa::atom atoms} * @brief Shows the usage of {@link cppa::atom atoms}
* and {@link cppa::arg_match arg_match}. * and {@link cppa::arg_match arg_match}.
* @example math_actor_example.cpp * @example math_actor.cpp
*/ */
/** /**
...@@ -417,11 +418,11 @@ inline void send_impl(T* whom, any_tuple&& what) { ...@@ -417,11 +418,11 @@ inline void send_impl(T* whom, any_tuple&& what) {
if (whom) self->send_message(whom, std::move(what)); if (whom) self->send_message(whom, std::move(what));
} }
template<typename T, typename... Args> template<typename T, typename... Ts>
inline void send_tpl_impl(T* whom, Args&&... what) { inline void send_tpl_impl(T* whom, Ts&&... what) {
static_assert(sizeof...(Args) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
if (whom) self->send_message(whom, if (whom) self->send_message(whom,
make_any_tuple(std::forward<Args>(what)...)); make_any_tuple(std::forward<Ts>(what)...));
} }
} // namespace detail } // namespace detail
...@@ -436,7 +437,7 @@ inline void send_tpl_impl(T* whom, Args&&... what) { ...@@ -436,7 +437,7 @@ inline void send_tpl_impl(T* whom, Args&&... what) {
* @param whom Receiver of the message. * @param whom Receiver of the message.
* @param what Message content as tuple. * @param what Message content as tuple.
*/ */
template<class C, typename... Args> template<class C, typename... Ts>
inline typename std::enable_if<std::is_base_of<channel, C>::value>::type inline typename std::enable_if<std::is_base_of<channel, C>::value>::type
send_tuple(const intrusive_ptr<C>& whom, any_tuple what) { send_tuple(const intrusive_ptr<C>& whom, any_tuple what) {
detail::send_impl(whom.get(), std::move(what)); detail::send_impl(whom.get(), std::move(what));
...@@ -446,12 +447,12 @@ send_tuple(const intrusive_ptr<C>& whom, any_tuple what) { ...@@ -446,12 +447,12 @@ send_tuple(const intrusive_ptr<C>& whom, any_tuple what) {
* @brief Sends <tt>{what...}</tt> as a message to @p whom. * @brief Sends <tt>{what...}</tt> as a message to @p whom.
* @param whom Receiver of the message. * @param whom Receiver of the message.
* @param what Message elements. * @param what Message elements.
* @pre <tt>sizeof...(Args) > 0</tt> * @pre <tt>sizeof...(Ts) > 0</tt>
*/ */
template<class C, typename... Args> template<class C, typename... Ts>
inline typename std::enable_if<std::is_base_of<channel, C>::value>::type inline typename std::enable_if<std::is_base_of<channel, C>::value>::type
send(const intrusive_ptr<C>& whom, Args&&... what) { send(const intrusive_ptr<C>& whom, Ts&&... what) {
detail::send_tpl_impl(whom.get(), std::forward<Args>(what)...); detail::send_tpl_impl(whom.get(), std::forward<Ts>(what)...);
} }
/** /**
...@@ -460,9 +461,9 @@ send(const intrusive_ptr<C>& whom, Args&&... what) { ...@@ -460,9 +461,9 @@ send(const intrusive_ptr<C>& whom, Args&&... what) {
* @param from Sender as seen by @p whom. * @param from Sender as seen by @p whom.
* @param whom Receiver of the message. * @param whom Receiver of the message.
* @param what Message elements. * @param what Message elements.
* @pre <tt>sizeof...(Args) > 0</tt> * @pre <tt>sizeof...(Ts) > 0</tt>
*/ */
template<class C, typename... Args> template<class C, typename... Ts>
inline typename std::enable_if<std::is_base_of<channel, C>::value>::type inline typename std::enable_if<std::is_base_of<channel, C>::value>::type
send_tuple_as(const actor_ptr& from, const intrusive_ptr<C>& whom, any_tuple what) { send_tuple_as(const actor_ptr& from, const intrusive_ptr<C>& whom, any_tuple what) {
if (whom) whom->enqueue(from.get(), std::move(what)); if (whom) whom->enqueue(from.get(), std::move(what));
...@@ -474,12 +475,12 @@ send_tuple_as(const actor_ptr& from, const intrusive_ptr<C>& whom, any_tuple wha ...@@ -474,12 +475,12 @@ send_tuple_as(const actor_ptr& from, const intrusive_ptr<C>& whom, any_tuple wha
* @param from Sender as seen by @p whom. * @param from Sender as seen by @p whom.
* @param whom Receiver of the message. * @param whom Receiver of the message.
* @param what Message elements. * @param what Message elements.
* @pre <tt>sizeof...(Args) > 0</tt> * @pre <tt>sizeof...(Ts) > 0</tt>
*/ */
template<class C, typename... Args> template<class C, typename... Ts>
inline typename std::enable_if<std::is_base_of<channel, C>::value>::type inline typename std::enable_if<std::is_base_of<channel, C>::value>::type
send_as(const actor_ptr& from, const intrusive_ptr<C>& whom, Args&&... what) { send_as(const actor_ptr& from, const intrusive_ptr<C>& whom, Ts&&... what) {
send_tuple_as(from, whom, make_any_tuple(std::forward<Args>(what)...)); send_tuple_as(from, whom, make_any_tuple(std::forward<Ts>(what)...));
} }
/** /**
...@@ -522,13 +523,13 @@ inline message_future sync_send_tuple(const actor_ptr& whom, any_tuple what) { ...@@ -522,13 +523,13 @@ inline message_future sync_send_tuple(const actor_ptr& whom, any_tuple what) {
* @returns A handle identifying a future to the response of @p whom. * @returns A handle identifying a future to the response of @p whom.
* @warning The returned handle is actor specific and the response to the sent * @warning The returned handle is actor specific and the response to the sent
* message cannot be received by another actor. * message cannot be received by another actor.
* @pre <tt>sizeof...(Args) > 0</tt> * @pre <tt>sizeof...(Ts) > 0</tt>
* @throws std::invalid_argument if <tt>whom == nullptr</tt> * @throws std::invalid_argument if <tt>whom == nullptr</tt>
*/ */
template<typename... Args> template<typename... Ts>
inline message_future sync_send(const actor_ptr& whom, Args&&... what) { inline message_future sync_send(const actor_ptr& whom, Ts&&... what) {
static_assert(sizeof...(Args) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
return sync_send_tuple(whom, make_any_tuple(std::forward<Args>(what)...)); return sync_send_tuple(whom, make_any_tuple(std::forward<Ts>(what)...));
} }
/** /**
...@@ -543,7 +544,7 @@ inline message_future sync_send(const actor_ptr& whom, Args&&... what) { ...@@ -543,7 +544,7 @@ inline message_future sync_send(const actor_ptr& whom, Args&&... what) {
* message cannot be received by another actor. * message cannot be received by another actor.
* @throws std::invalid_argument if <tt>whom == nullptr</tt> * @throws std::invalid_argument if <tt>whom == nullptr</tt>
*/ */
template<class Rep, class Period, typename... Args> template<class Rep, class Period, typename... Ts>
message_future timed_sync_send_tuple(const actor_ptr& whom, message_future timed_sync_send_tuple(const actor_ptr& whom,
const std::chrono::duration<Rep,Period>& rel_time, const std::chrono::duration<Rep,Period>& rel_time,
any_tuple what) { any_tuple what) {
...@@ -564,17 +565,17 @@ message_future timed_sync_send_tuple(const actor_ptr& whom, ...@@ -564,17 +565,17 @@ message_future timed_sync_send_tuple(const actor_ptr& whom,
* @returns A handle identifying a future to the response of @p whom. * @returns A handle identifying a future to the response of @p whom.
* @warning The returned handle is actor specific and the response to the sent * @warning The returned handle is actor specific and the response to the sent
* message cannot be received by another actor. * message cannot be received by another actor.
* @pre <tt>sizeof...(Args) > 0</tt> * @pre <tt>sizeof...(Ts) > 0</tt>
* @throws std::invalid_argument if <tt>whom == nullptr</tt> * @throws std::invalid_argument if <tt>whom == nullptr</tt>
*/ */
template<class Rep, class Period, typename... Args> template<class Rep, class Period, typename... Ts>
message_future timed_sync_send(const actor_ptr& whom, message_future timed_sync_send(const actor_ptr& whom,
const std::chrono::duration<Rep,Period>& rel_time, const std::chrono::duration<Rep,Period>& rel_time,
Args&&... what) { Ts&&... what) {
static_assert(sizeof...(Args) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
return timed_sync_send_tuple(whom, return timed_sync_send_tuple(whom,
rel_time, rel_time,
make_any_tuple(std::forward<Args>(what)...)); make_any_tuple(std::forward<Ts>(what)...));
} }
/** /**
...@@ -589,24 +590,25 @@ inline void reply_tuple(any_tuple what) { ...@@ -589,24 +590,25 @@ inline void reply_tuple(any_tuple what) {
* @brief Sends a message to the sender of the last received message. * @brief Sends a message to the sender of the last received message.
* @param what Message elements. * @param what Message elements.
*/ */
template<typename... Args> template<typename... Ts>
inline void reply(Args&&... what) { inline void reply(Ts&&... what) {
self->reply_message(make_any_tuple(std::forward<Args>(what)...)); self->reply_message(make_any_tuple(std::forward<Ts>(what)...));
} }
/** /**
* @brief Sends a message as reply to @p handle. * @brief Sends a message as reply to @p handle.
*/ */
template<typename... Args> template<typename... Ts>
inline void reply_to(const response_handle& handle, Args&&... what) { inline void reply_to(const response_handle& handle, Ts&&... what) {
if (handle.valid()) { if (handle.valid()) {
handle.apply(make_any_tuple(std::forward<Args>(what)...)); handle.apply(make_any_tuple(std::forward<Ts>(what)...));
} }
} }
/** /**
* @brief Sends a message to the sender of the last received message. * @brief Replies with @p what to @p handle.
* @param what Message content as a tuple. * @param handle Identifies a previously received request.
* @param what Response message.
*/ */
inline void reply_tuple_to(const response_handle& handle, any_tuple what) { inline void reply_tuple_to(const response_handle& handle, any_tuple what) {
handle.apply(std::move(what)); handle.apply(std::move(what));
...@@ -626,7 +628,7 @@ inline void forward_to(const actor_ptr& whom) { ...@@ -626,7 +628,7 @@ inline void forward_to(const actor_ptr& whom) {
* microseconds, milliseconds, seconds or minutes. * microseconds, milliseconds, seconds or minutes.
* @param what Message content as a tuple. * @param what Message content as a tuple.
*/ */
template<class Rep, class Period, typename... Args> template<class Rep, class Period, typename... Ts>
inline void delayed_send_tuple(const channel_ptr& whom, inline void delayed_send_tuple(const channel_ptr& whom,
const std::chrono::duration<Rep,Period>& rtime, const std::chrono::duration<Rep,Period>& rtime,
any_tuple what) { any_tuple what) {
...@@ -640,15 +642,15 @@ inline void delayed_send_tuple(const channel_ptr& whom, ...@@ -640,15 +642,15 @@ inline void delayed_send_tuple(const channel_ptr& whom,
* microseconds, milliseconds, seconds or minutes. * microseconds, milliseconds, seconds or minutes.
* @param what Message elements. * @param what Message elements.
*/ */
template<class Rep, class Period, typename... Args> template<class Rep, class Period, typename... Ts>
inline void delayed_send(const channel_ptr& whom, inline void delayed_send(const channel_ptr& whom,
const std::chrono::duration<Rep,Period>& rtime, const std::chrono::duration<Rep,Period>& rtime,
Args&&... what) { Ts&&... what) {
static_assert(sizeof...(Args) > 0, "no message to send"); static_assert(sizeof...(Ts) > 0, "no message to send");
if (whom) { if (whom) {
delayed_send_tuple(whom, delayed_send_tuple(whom,
rtime, rtime,
make_any_tuple(std::forward<Args>(what)...)); make_any_tuple(std::forward<Ts>(what)...));
} }
} }
...@@ -659,7 +661,7 @@ inline void delayed_send(const channel_ptr& whom, ...@@ -659,7 +661,7 @@ inline void delayed_send(const channel_ptr& whom,
* @param what Message content as a tuple. * @param what Message content as a tuple.
* @see delayed_send() * @see delayed_send()
*/ */
template<class Rep, class Period, typename... Args> template<class Rep, class Period, typename... Ts>
inline void delayed_reply_tuple(const std::chrono::duration<Rep, Period>& rtime, inline void delayed_reply_tuple(const std::chrono::duration<Rep, Period>& rtime,
any_tuple what) { any_tuple what) {
get_scheduler()->delayed_reply(self->last_sender(), get_scheduler()->delayed_reply(self->last_sender(),
...@@ -675,28 +677,31 @@ inline void delayed_reply_tuple(const std::chrono::duration<Rep, Period>& rtime, ...@@ -675,28 +677,31 @@ inline void delayed_reply_tuple(const std::chrono::duration<Rep, Period>& rtime,
* @param what Message elements. * @param what Message elements.
* @see delayed_send() * @see delayed_send()
*/ */
template<class Rep, class Period, typename... Args> template<class Rep, class Period, typename... Ts>
inline void delayed_reply(const std::chrono::duration<Rep, Period>& rtime, inline void delayed_reply(const std::chrono::duration<Rep, Period>& rtime,
Args&&... what) { Ts&&... what) {
delayed_reply_tuple(rtime, make_any_tuple(std::forward<Args>(what)...)); delayed_reply_tuple(rtime, make_any_tuple(std::forward<Ts>(what)...));
} }
/** @} */ /** @} */
#ifndef CPPA_DOCUMENTATION
// matches "send(this, ...)" and "send(self, ...)" // matches "send(this, ...)" and "send(self, ...)"
inline void send_tuple(local_actor* whom, any_tuple what) { inline void send_tuple(local_actor* whom, any_tuple what) {
detail::send_impl(whom, std::move(what)); detail::send_impl(whom, std::move(what));
} }
template<typename... Args> template<typename... Ts>
inline void send(local_actor* whom, Args&&... args) { inline void send(local_actor* whom, Ts&&... args) {
detail::send_tpl_impl(whom, std::forward<Args>(args)...); detail::send_tpl_impl(whom, std::forward<Ts>(args)...);
} }
inline const self_type& operator<<(const self_type& s, any_tuple what) { inline const self_type& operator<<(const self_type& s, any_tuple what) {
detail::send_impl(s.get(), std::move(what)); detail::send_impl(s.get(), std::move(what));
return s; return s;
} }
#endif // CPPA_DOCUMENTATION inline actor_ptr eval_sopts(spawn_options opts, actor_ptr ptr) {
if (has_monitor_flag(opts)) self->monitor(ptr);
if (has_link_flag(opts)) self->link_to(ptr);
return std::move(ptr);
}
/** /**
* @ingroup ActorCreation * @ingroup ActorCreation
...@@ -704,213 +709,66 @@ inline const self_type& operator<<(const self_type& s, any_tuple what) { ...@@ -704,213 +709,66 @@ inline const self_type& operator<<(const self_type& s, any_tuple what) {
*/ */
/** /**
* @brief Spawns a new context-switching or thread-mapped {@link actor} * @brief Spawns a new {@link actor} that evaluates given arguments.
* that executes <tt>fun(args...)</tt>. * @param args A functor followed by its arguments.
* @param fun A function implementing the actor's behavior. * @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @param args Optional function parameters for @p fun.
* @tparam Hint A hint to the scheduler for the best scheduling strategy.
* @returns An {@link actor_ptr} to the spawned {@link actor}. * @returns An {@link actor_ptr} to the spawned {@link actor}.
*/ */
template<scheduling_hint Hint, typename Fun, typename... Args> template<spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn(Fun&& fun, Args&&... args) { actor_ptr spawn(Ts&&... args) {
return get_scheduler()->spawn_impl(Hint, static_assert(sizeof...(Ts) > 0, "too few arguments provided");
std::forward<Fun>(fun), return eval_sopts(Options,
std::forward<Args>(args)...); get_scheduler()->exec(Options,
scheduler::init_callback{},
std::forward<Ts>(args)...));
} }
/** /**
* @brief Spawns a new context-switching {@link actor} * @brief Spawns an actor of type @p Impl.
* that executes <tt>fun(args...)</tt>. * @param args Constructor arguments.
* @param fun A function implementing the actor's behavior. * @tparam Impl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @param args Optional function parameters for @p fun. * @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @returns An {@link actor_ptr} to the spawned {@link actor}. * @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note This function is equal to <tt>spawn<scheduled>(fun, args...)</tt>.
*/ */
template<typename Fun, typename... Args> template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn(Fun&& fun, Args&&... args) { actor_ptr spawn(Ts&&... args) {
return spawn<scheduled>(std::forward<Fun>(fun), auto rawptr = detail::memory::create<Impl>(std::forward<Ts>(args)...);
std::forward<Args>(args)...); return eval_sopts(Options, get_scheduler()->exec(Options, rawptr));
} }
/** /**
* @brief Spawns a new context-switching or thread-mapped {@link actor} * @brief Spawns a new actor that evaluates given arguments and
* that executes <tt>fun(args...)</tt> and * immediately joins @p grp.
* joins @p grp immediately. * @param args A functor followed by its arguments.
* @param fun A function implementing the actor's behavior. * @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @param args Optional function parameters for @p fun.
* @tparam Hint A hint to the scheduler for the best scheduling strategy.
* @returns An {@link actor_ptr} to the spawned {@link actor}. * @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note The spawned actor joins @p grp after its * @note The spawned has joined the group before this function returns.
* {@link local_actor::init() init} member function is called but
* before it is executed. Hence, the spawned actor already joined
* the group before this function returns.
*/ */
template<scheduling_hint Hint, typename Fun, typename... Args> template<spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn_in_group(const group_ptr& grp, Fun&& fun, Args&&... args) { actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) {
return get_scheduler()->spawn_cb_impl(Hint, static_assert(sizeof...(Ts) > 0, "too few arguments provided");
[grp](local_actor* ptr) { auto init_cb = [=](local_actor* ptr) {
ptr->join(grp); ptr->join(grp);
}, };
std::forward<Fun>(fun), return eval_sopts(Options,
std::forward<Args>(args)...); get_scheduler()->exec(Options,
} init_cb,
std::forward<Ts>(args)...));
/**
* @brief Spawns a new context-switching {@link actor}
* that executes <tt>fun(args...)</tt> and
* joins @p grp immediately.
* @param fun A function implementing the actor's behavior.
* @param args Optional function parameters for @p fun.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note The spawned actor joins @p grp after its
* {@link local_actor::init() init} member function is called but
* before it is executed. Hence, the spawned actor already joined
* the group before this function returns.
* @note This function is equal to
* <tt>spawn_in_group<scheduled>(fun, args...)</tt>.
*/
template<typename Fun, typename... Args>
actor_ptr spawn_in_group(Fun&& fun, Args&&... args) {
return spawn_in_group<scheduled>(std::forward<Fun>(fun),
std::forward<Args>(args)...);
}
/**
* @brief Spawns an actor of type @p ActorImpl.
* @param args Optional constructor arguments.
* @tparam ActorImpl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
*/
template<class ActorImpl, typename... Args>
actor_ptr spawn(Args&&... args) {
auto ptr = detail::memory::create<ActorImpl>(std::forward<Args>(args)...);
return get_scheduler()->spawn(ptr);
}
/**
* @brief Spawns an actor of type @p ActorImpl that joins @p grp immediately.
* @param grp The group that the newly created actor shall join.
* @param args Optional constructor arguments.
* @tparam ActorImpl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note The spawned actor joins @p grp after its
* {@link local_actor::init() init} member function is called but
* before it is executed. Hence, the spawned actor already joined
* the group before this function returns.
*/
template<class ActorImpl, typename... Args>
actor_ptr spawn_in_group(const group_ptr& grp, Args&&... args) {
auto ptr = detail::memory::create<ActorImpl>(std::forward<Args>(args)...);
return get_scheduler()->spawn(ptr, [&](local_actor* p) { p->join(grp); });
}
#ifndef CPPA_DOCUMENTATION
template<class ActorImpl, typename... Args>
actor_ptr spawn_hidden_in_group(const group_ptr& grp, Args&&... args) {
auto ptr = detail::memory::create<ActorImpl>(std::forward<Args>(args)...);
return get_scheduler()->spawn(ptr, [&](local_actor* p) { p->join(grp); },
scheduled_and_hidden);
}
template<class ActorImpl, typename... Args>
actor_ptr spawn_hidden(Args&&... args) {
auto ptr = detail::memory::create<ActorImpl>(std::forward<Args>(args)...);
return get_scheduler()->spawn(ptr, scheduled_and_hidden);
}
#endif
/**
* @brief Spawns a new context-switching or thread-mapped {@link actor}
* that executes <tt>fun(args...)</tt> and creates a link between the
* calling actor and the new actor.
* @param fun A function implementing the actor's behavior.
* @param args Optional function parameters for @p fun.
* @tparam Hint A hint to the scheduler for the best scheduling strategy.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
*/
template<scheduling_hint Hint, typename Fun, typename... Args>
actor_ptr spawn_link(Fun&& fun, Args&&... args) {
auto res = spawn<Hint>(std::forward<Fun>(fun), std::forward<Args>(args)...);
self->link_to(res);
return res;
}
/**
* @brief Spawns a new context-switching {@link actor}
* that executes <tt>fun(args...)</tt> and creates a link between the
* calling actor and the new actor.
* @param fun A function implementing the actor's behavior.
* @param args Optional function parameters for @p fun.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note This function is equal to <tt>spawn<scheduled>(fun, args...)</tt>.
*/
template<typename Fun, typename... Args>
actor_ptr spawn_link(Fun&& fun, Args&&... args) {
auto res = spawn(std::forward<Fun>(fun), std::forward<Args>(args)...);
self->link_to(res);
return res;
}
/**
* @brief Spawns an actor of type @p ActorImpl and creates a link between the
* calling actor and the new actor.
* @param args Optional constructor arguments.
* @tparam ActorImpl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
*/
template<class ActorImpl, typename... Args>
actor_ptr spawn_link(Args&&... args) {
auto res = spawn<ActorImpl>(std::forward<Args>(args)...);
self->link_to(res);
return res;
} }
/** /**
* @brief Spawns a new context-switching or thread-mapped {@link actor} * @brief Spawns an actor of type @p Impl that immediately joins @p grp.
* that executes <tt>fun(args...)</tt> and adds a monitor to the * @param args Constructor arguments.
* new actor. * @tparam Impl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @param fun A function implementing the actor's behavior. * @tparam Options Optional flags to modify <tt>spawn</tt>'s behavior.
* @param args Optional function parameters for @p fun.
* @tparam Hint A hint to the scheduler for the best scheduling strategy.
* @returns An {@link actor_ptr} to the spawned {@link actor}. * @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note The spawned has joined the group before this function returns.
*/ */
template<scheduling_hint Hint, typename Fun, typename... Args> template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn_monitor(Fun&& fun, Args&&... args) { actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) {
auto res = spawn<Hint>(std::forward<Fun>(fun), std::forward<Args>(args)...); auto rawptr = detail::memory::create<Impl>(std::forward<Ts>(args)...);
self->monitor(res); rawptr->join(grp);
return res; return eval_sopts(Options, get_scheduler()->exec(Options, rawptr));
}
/**
* @brief Spawns a new context-switching {@link actor}
* that executes <tt>fun(args...)</tt> and adds a monitor to the
* new actor.
* @param fun A function implementing the actor's behavior.
* @param args Optional function parameters for @p fun.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note This function is equal to <tt>spawn<scheduled>(fun, args...)</tt>.
*/
template<typename Fun, typename... Args>
actor_ptr spawn_monitor(Fun&& fun, Args&&... args) {
auto res = spawn(std::forward<Fun>(fun), std::forward<Args>(args)...);
self->monitor(res);
return res;
}
/**
* @brief Spawns an actor of type @p ActorImpl and adds a monitor to the
* new actor.
* @param args Optional constructor arguments.
* @tparam ActorImpl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @returns An {@link actor_ptr} to the spawned {@link actor}.
*/
template<class ActorImpl, typename... Args>
actor_ptr spawn_monitor(Args&&... args) {
auto res = spawn<ActorImpl>(std::forward<Args>(args)...);
self->monitor(res);
return res;
} }
/** @} */ /** @} */
...@@ -922,7 +780,8 @@ actor_ptr spawn_monitor(Args&&... args) { ...@@ -922,7 +780,8 @@ actor_ptr spawn_monitor(Args&&... args) {
* @warning Do not call this function in cooperatively scheduled actors. * @warning Do not call this function in cooperatively scheduled actors.
*/ */
inline void await_all_others_done() { 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);
} }
/** /**
...@@ -988,6 +847,13 @@ inline void quit_actor(const actor_ptr& whom, std::uint32_t reason) { ...@@ -988,6 +847,13 @@ inline void quit_actor(const actor_ptr& whom, std::uint32_t reason) {
send(whom, atom("EXIT"), reason); send(whom, atom("EXIT"), reason);
} }
template<typename... Ts>
inline void become(Ts&&... args) {
self->become(std::forward<Ts>(args)...);
}
inline void unbecome() { self->unbecome(); }
struct actor_ostream { struct actor_ostream {
typedef const actor_ostream& (*fun_type)(const actor_ostream&); typedef const actor_ostream& (*fun_type)(const actor_ostream&);
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 @@ ...@@ -44,18 +44,11 @@
namespace cppa { namespace detail { namespace cppa { namespace detail {
enum tuple_impl_info {
statically_typed,
dynamically_typed
};
class abstract_tuple : public ref_counted { class abstract_tuple : public ref_counted {
tuple_impl_info m_impl_type;
public: public:
inline abstract_tuple(tuple_impl_info tii) : m_impl_type(tii) { } abstract_tuple(bool dynamically_typed);
abstract_tuple(const abstract_tuple& other); abstract_tuple(const abstract_tuple& other);
// mutators // mutators
...@@ -76,7 +69,7 @@ class abstract_tuple : public ref_counted { ...@@ -76,7 +69,7 @@ class abstract_tuple : public ref_counted {
// A statically typed tuple implementation can use some optimizations, // A statically typed tuple implementation can use some optimizations,
// e.g., "impl_type() == statically_typed" implies that type_token() // e.g., "impl_type() == statically_typed" implies that type_token()
// identifies all possible instances of a given tuple implementation // 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 // uniquely identifies this category (element types) of messages
// override this member function only if impl_type() == statically_typed // override this member function only if impl_type() == statically_typed
...@@ -93,6 +86,10 @@ class abstract_tuple : public ref_counted { ...@@ -93,6 +86,10 @@ class abstract_tuple : public ref_counted {
inline const_iterator end() const { return {this, size()}; } inline const_iterator end() const { return {this, size()}; }
inline const_iterator cend() const { return {this, size()}; } inline const_iterator cend() const { return {this, size()}; }
private:
bool m_is_dynamic;
}; };
struct full_eq_type { struct full_eq_type {
......
...@@ -195,6 +195,8 @@ class continuation_decorator : public behavior_impl { ...@@ -195,6 +195,8 @@ class continuation_decorator : public behavior_impl {
}; };
typedef intrusive_ptr<behavior_impl> behavior_impl_ptr;
} } // namespace cppa::detail } } // namespace cppa::detail
#endif // BEHAVIOR_IMPL_HPP #endif // BEHAVIOR_IMPL_HPP
...@@ -122,7 +122,7 @@ class behavior_stack ...@@ -122,7 +122,7 @@ class behavior_stack
template<class Policy, class Client> template<class Policy, class Client>
void exec(Policy& policy, Client* client) { void exec(Policy& policy, Client* client) {
while (!empty()) { while (!empty()) {
invoke(policy, client, client->receive_node()); invoke(policy, client, client->await_message());
cleanup(); cleanup();
} }
} }
...@@ -140,7 +140,7 @@ class behavior_stack ...@@ -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 // base iterator points to the element *after* the correct element
if (i != m_elements.rend()) erase_at(i.base() - 1); if (i != m_elements.rend()) erase_at(i.base() - 1);
} }
...@@ -152,7 +152,7 @@ class behavior_stack ...@@ -152,7 +152,7 @@ class behavior_stack
template<typename UnaryPredicate> template<typename UnaryPredicate>
inline void rerase_if(UnaryPredicate p) { 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 { ...@@ -50,7 +50,7 @@ class container_tuple_view : public abstract_tuple {
typedef typename Container::value_type value_type; typedef typename Container::value_type value_type;
container_tuple_view(Container* c, bool take_ownership = false) 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); CPPA_REQUIRE(c != nullptr);
if (!take_ownership) m_ptr.get_deleter().disable(); if (!take_ownership) m_ptr.get_deleter().disable();
} }
......
...@@ -105,7 +105,7 @@ class decorated_tuple : public abstract_tuple { ...@@ -105,7 +105,7 @@ class decorated_tuple : public abstract_tuple {
vector_type m_mapping; vector_type m_mapping;
decorated_tuple(cow_pointer_type d, const vector_type& v) 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) { , m_decorated(std::move(d)), m_mapping(v) {
# ifdef CPPA_DEBUG # ifdef CPPA_DEBUG
const cow_pointer_type& ptr = m_decorated; // prevent detaching const cow_pointer_type& ptr = m_decorated; // prevent detaching
...@@ -116,7 +116,7 @@ class decorated_tuple : public abstract_tuple { ...@@ -116,7 +116,7 @@ class decorated_tuple : public abstract_tuple {
} }
decorated_tuple(cow_pointer_type d, size_t offset) 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 # ifdef CPPA_DEBUG
const cow_pointer_type& ptr = m_decorated; // prevent detaching const cow_pointer_type& ptr = m_decorated; // prevent detaching
# endif # endif
......
...@@ -104,8 +104,10 @@ class event_based_actor_factory { ...@@ -104,8 +104,10 @@ class event_based_actor_factory {
template<typename... Args> template<typename... Args>
actor_ptr spawn(Args&&... args) { actor_ptr spawn(Args&&... args) {
return get_scheduler()->spawn(memory::create<impl>(m_init, m_on_exit, auto ptr = memory::create<impl>(m_init,
std::forward<Args>(args)...)); m_on_exit,
std::forward<Args>(args)...);
return get_scheduler()->exec(no_spawn_options, ptr);
} }
private: private:
......
...@@ -46,7 +46,7 @@ struct matcher; ...@@ -46,7 +46,7 @@ struct matcher;
template<class Tuple, typename... T> template<class Tuple, typename... T>
struct matcher<wildcard_position::nil, Tuple, T...> { struct matcher<wildcard_position::nil, Tuple, T...> {
static inline bool tmatch(const Tuple& tup) { 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...>) // statically typed tuples return &typeid(type_list<T...>)
// as type token // as type token
return typeid(util::type_list<T...>) == *(tup.type_token()); return typeid(util::type_list<T...>) == *(tup.type_token());
......
...@@ -108,7 +108,7 @@ class do_receive_helper { ...@@ -108,7 +108,7 @@ class do_receive_helper {
template<typename... Args> template<typename... Args>
do_receive_helper(Args&&... 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; do_receive_helper(do_receive_helper&&) = default;
...@@ -122,21 +122,6 @@ class do_receive_helper { ...@@ -122,21 +122,6 @@ class do_receive_helper {
sptr->dequeue(m_bhvr); sptr->dequeue(m_bhvr);
} }
while (stmt() == false); 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 @@ ...@@ -36,6 +36,7 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include "cppa/logging.hpp"
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/message_id.hpp" #include "cppa/message_id.hpp"
...@@ -135,7 +136,7 @@ class receive_policy { ...@@ -135,7 +136,7 @@ class receive_policy {
template<class Client, class FunOrBehavior> template<class Client, class FunOrBehavior>
inline void receive_wo_timeout(Client *client, FunOrBehavior& fun) { inline void receive_wo_timeout(Client *client, FunOrBehavior& fun) {
if (!invoke_from_cache(client, 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 { ...@@ -152,7 +153,7 @@ class receive_policy {
else if (!invoke_from_cache(client, bhvr)) { else if (!invoke_from_cache(client, bhvr)) {
if (bhvr.timeout().is_zero()) { if (bhvr.timeout().is_zero()) {
pointer e = nullptr; pointer e = nullptr;
while ((e = client->try_receive_node()) != nullptr) { while ((e = client->m_mailbox.try_pop()) != nullptr) {
CPPA_REQUIRE(e->marked == false); CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr)) { if (invoke(client, e, bhvr)) {
return; // done return; // done
...@@ -163,7 +164,7 @@ class receive_policy { ...@@ -163,7 +164,7 @@ class receive_policy {
else { else {
auto timeout = client->init_timeout(bhvr.timeout()); auto timeout = client->init_timeout(bhvr.timeout());
pointer e = nullptr; pointer e = nullptr;
while ((e = client->try_receive_node(timeout)) != nullptr) { while ((e = client->await_message(timeout)) != nullptr) {
CPPA_REQUIRE(e->marked == false); CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr)) { if (invoke(client, e, bhvr)) {
return; // done return; // done
...@@ -182,7 +183,7 @@ class receive_policy { ...@@ -182,7 +183,7 @@ class receive_policy {
CPPA_REQUIRE(bhvr.timeout().is_zero() == false); CPPA_REQUIRE(bhvr.timeout().is_zero() == false);
auto timeout = client->init_timeout(bhvr.timeout()); auto timeout = client->init_timeout(bhvr.timeout());
pointer e = nullptr; pointer e = nullptr;
while ((e = client->try_receive_node(timeout)) != nullptr) { while ((e = client->await_message(timeout)) != nullptr) {
CPPA_REQUIRE(e->marked == false); CPPA_REQUIRE(e->marked == false);
if (invoke(client, e, bhvr, mid)) { if (invoke(client, e, bhvr, mid)) {
return; // done return; // done
...@@ -190,9 +191,7 @@ class receive_policy { ...@@ -190,9 +191,7 @@ class receive_policy {
} }
handle_timeout(client, bhvr); handle_timeout(client, bhvr);
} }
else { else while (!invoke(client, client->await_message(), bhvr, mid)) { }
while (!invoke(client, client->receive_node(), bhvr, mid)) { }
}
} }
} }
...@@ -243,9 +242,8 @@ class receive_policy { ...@@ -243,9 +242,8 @@ class receive_policy {
CPPA_REQUIRE(!message_id.valid()); CPPA_REQUIRE(!message_id.valid());
if (client->m_trap_exit == false) { if (client->m_trap_exit == false) {
if (v1 != exit_reason::normal) { if (v1 != exit_reason::normal) {
// TODO: check for possible memory leak here
// ('node' might not get destroyed)
client->quit(v1); client->quit(v1);
return non_normal_exit_signal;
} }
return normal_exit_signal; return normal_exit_signal;
} }
...@@ -327,7 +325,7 @@ class receive_policy { ...@@ -327,7 +325,7 @@ class receive_policy {
if (client->has_behavior()) { if (client->has_behavior()) {
client->request_timeout(client->get_behavior().timeout()); client->request_timeout(client->get_behavior().timeout());
} }
else client->m_has_pending_timeout_request = false; else client->reset_timeout();
} }
template<class Client> template<class Client>
...@@ -350,12 +348,25 @@ class receive_policy { ...@@ -350,12 +348,25 @@ class receive_policy {
} }
switch (this->filter_msg(client, node)) { switch (this->filter_msg(client, node)) {
default: { default: {
// one of: CPPA_CRITICAL("illegal filter result");
// - normal_exit_signal }
// - expired_sync_response case normal_exit_signal: {
// - expired_timeout_message 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; 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: { case timeout_message: {
handle_timeout(client, fun); handle_timeout(client, fun);
if (awaited_response.valid()) { if (awaited_response.valid()) {
...@@ -372,6 +383,7 @@ class receive_policy { ...@@ -372,6 +383,7 @@ class receive_policy {
if (awaited_response.valid() && node->mid == awaited_response) { if (awaited_response.valid() && node->mid == awaited_response) {
auto previous_node = hm_begin(client, node, policy); auto previous_node = hm_begin(client, node, policy);
if (!fun(node->msg) && handle_sync_failure_on_mismatch) { if (!fun(node->msg) && handle_sync_failure_on_mismatch) {
CPPA_LOG_WARNING("sync failure occured");
client->handle_sync_failure(); client->handle_sync_failure();
} }
client->mark_arrived(awaited_response); client->mark_arrived(awaited_response);
...@@ -390,6 +402,8 @@ class receive_policy { ...@@ -390,6 +402,8 @@ class receive_policy {
auto id = node->mid; auto id = node->mid;
auto sender = node->sender; auto sender = node->sender;
if (id.valid() && !id.is_answered() && sender) { if (id.valid() && !id.is_answered() && sender) {
CPPA_LOG_WARNING("actor did not reply to a "
"synchronous request message");
sender->sync_enqueue(client, sender->sync_enqueue(client,
id.response_id(), id.response_id(),
make_any_tuple(atom("VOID"))); make_any_tuple(atom("VOID")));
......
...@@ -37,14 +37,14 @@ ...@@ -37,14 +37,14 @@
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/message_id.hpp" #include "cppa/message_id.hpp"
#include "cppa/ref_counted.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 // needs access to constructor + destructor to initialize m_dummy_node
namespace cppa { class local_actor; } namespace cppa { class local_actor; }
namespace cppa { namespace detail { 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 memory;
friend class ::cppa::local_actor; friend class ::cppa::local_actor;
...@@ -64,11 +64,18 @@ class recursive_queue_node : public memory_cached_mixin<memory_managed> { ...@@ -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=(recursive_queue_node&&) = delete;
recursive_queue_node& operator=(const 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: private:
recursive_queue_node() = default; 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 @@ ...@@ -31,22 +31,19 @@
#ifndef CPPA_SCHEDULED_ACTOR_DUMMY_HPP #ifndef CPPA_SCHEDULED_ACTOR_DUMMY_HPP
#define 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 { 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&); resume_result resume(util::fiber*, actor_ptr&);
void quit(std::uint32_t); void quit(std::uint32_t);
void dequeue(behavior&); void dequeue(behavior&);
void dequeue(partial_function&); void dequeue(partial_function&);
void dequeue_response(behavior&, message_id_t); 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 do_become(behavior&&, bool);
void become_waiting_for(behavior&&, message_id_t); void become_waiting_for(behavior&&, message_id_t);
bool has_behavior(); bool has_behavior();
......
...@@ -37,7 +37,6 @@ namespace cppa { ...@@ -37,7 +37,6 @@ namespace cppa {
class logging; class logging;
class scheduler; class scheduler;
class msg_content;
} // namespace cppa } // namespace cppa
...@@ -86,7 +85,7 @@ class singleton_manager { ...@@ -86,7 +85,7 @@ class singleton_manager {
private: private:
/** /*
* @brief Type @p T has to provide: <tt>static T* create_singleton()</tt>, * @brief Type @p T has to provide: <tt>static T* create_singleton()</tt>,
* <tt>void initialize()</tt>, <tt>void destroy()</tt>, * <tt>void initialize()</tt>, <tt>void destroy()</tt>,
* and <tt>dispose()</tt>. * and <tt>dispose()</tt>.
......
...@@ -28,22 +28,37 @@ ...@@ -28,22 +28,37 @@
\******************************************************************************/ \******************************************************************************/
#include "cppa/detail/actor_count.hpp" #ifndef CPPA_SYNC_REQUEST_BOUNCER_HPP
#include "cppa/detail/actor_registry.hpp" #define CPPA_SYNC_REQUEST_BOUNCER_HPP
#include "cppa/detail/singleton_manager.hpp"
namespace cppa { namespace detail { #include <cstdint>
void inc_actor_count() { #include "cppa/actor.hpp"
singleton_manager::get_actor_registry()->inc_running(); #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() { namespace cppa { namespace detail {
singleton_manager::get_actor_registry()->dec_running();
}
void actor_count_wait_until(size_t value) { struct sync_request_bouncer {
singleton_manager::get_actor_registry()->await_running_count_equal(value); 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 } } // namespace cppa::detail
#endif // CPPA_SYNC_REQUEST_BOUNCER_HPP
...@@ -176,8 +176,8 @@ struct tdata<> { ...@@ -176,8 +176,8 @@ struct tdata<> {
inline bool operator==(const tdata&) const { return true; } inline bool operator==(const tdata&) const { return true; }
inline tuple_impl_info impl_type() const { inline bool dynamically_typed() const {
return statically_typed; return false;
} }
}; };
......
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
#include "cppa/context_switching_actor.hpp" #include "cppa/context_switching_actor.hpp"
#include "cppa/util/producer_consumer_list.hpp" #include "cppa/util/producer_consumer_list.hpp"
#include "cppa/detail/scheduled_actor_dummy.hpp" #include "cppa/detail/scheduled_actor_dummy.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa { namespace detail { namespace cppa { namespace detail {
...@@ -47,31 +46,24 @@ class thread_pool_scheduler : public scheduler { ...@@ -47,31 +46,24 @@ class thread_pool_scheduler : public scheduler {
public: public:
using scheduler::init_callback;
using scheduler::void_function;
struct worker; struct worker;
thread_pool_scheduler(); thread_pool_scheduler();
thread_pool_scheduler(size_t num_worker_threads); thread_pool_scheduler(size_t num_worker_threads);
void initialize() /*override*/; void initialize();
void destroy() /*override*/;
void enqueue(scheduled_actor* what) /*override*/;
actor_ptr spawn(scheduled_actor* what, void destroy();
scheduling_hint hint);
actor_ptr spawn(scheduled_actor* what, void enqueue(scheduled_actor* what);
init_callback init_cb,
scheduling_hint hint);
actor_ptr spawn(void_function fun, actor_ptr exec(spawn_options opts, scheduled_actor_ptr ptr);
scheduling_hint hint);
actor_ptr spawn(void_function what, actor_ptr exec(spawn_options opts, init_callback init_cb, void_function f);
init_callback init_cb,
scheduling_hint hint);
private: private:
...@@ -86,10 +78,6 @@ class thread_pool_scheduler : public scheduler { ...@@ -86,10 +78,6 @@ class thread_pool_scheduler : public scheduler {
static void worker_loop(worker*); static void worker_loop(worker*);
static void supervisor_loop(job_queue*, scheduled_actor*, size_t); static void supervisor_loop(job_queue*, scheduled_actor*, size_t);
actor_ptr spawn_impl(scheduled_actor_ptr what);
actor_ptr spawn_as_thread(void_function fun, init_callback cb, bool hidden);
}; };
} } // namespace cppa::detail } } // namespace cppa::detail
......
...@@ -59,8 +59,7 @@ class tuple_vals : public abstract_tuple { ...@@ -59,8 +59,7 @@ class tuple_vals : public abstract_tuple {
tuple_vals(const tuple_vals&) = default; tuple_vals(const tuple_vals&) = default;
template<typename... Args> template<typename... Args>
tuple_vals(Args&&... args) tuple_vals(Args&&... args) : super(false)
: super(tuple_impl_info::statically_typed)
, m_data(std::forward<Args>(args)...) { } , m_data(std::forward<Args>(args)...) { }
const void* native_data() const { const void* native_data() const {
......
...@@ -68,9 +68,7 @@ class tuple_view : public abstract_tuple { ...@@ -68,9 +68,7 @@ class tuple_view : public abstract_tuple {
/** /**
* @warning @p tuple_view does @b NOT takes ownership for given pointers * @warning @p tuple_view does @b NOT takes ownership for given pointers
*/ */
tuple_view(Ts*... args) tuple_view(Ts*... args) : super(false), m_data(args...) { }
: super(tuple_impl_info::statically_typed), m_data(args...) {
}
inline data_type& data() { inline data_type& data() {
return m_data; return m_data;
......
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
\******************************************************************************/ \******************************************************************************/
#ifndef CPPA_ENABLE_WEAK_PTR_MIXIN_HPP #ifndef CPPA_ENABLE_WEAK_PTR_HPP
#define CPPA_ENABLE_WEAK_PTR_MIXIN_HPP #define CPPA_ENABLE_WEAK_PTR_HPP
#include <mutex> #include <mutex>
#include <utility> #include <utility>
...@@ -47,10 +47,8 @@ namespace cppa { ...@@ -47,10 +47,8 @@ namespace cppa {
/** /**
* @brief Enables derived classes to be used in {@link weak_intrusive_ptr}. * @brief Enables derived classes to be used in {@link weak_intrusive_ptr}.
*/ */
template<class Base> template<class Base, class Subtype>
class enable_weak_ptr_mixin : public Base { class enable_weak_ptr : public Base {
typedef Base super;
template<typename T> template<typename T>
friend class weak_intrusive_ptr; friend class weak_intrusive_ptr;
...@@ -60,9 +58,11 @@ class enable_weak_ptr_mixin : public Base { ...@@ -60,9 +58,11 @@ class enable_weak_ptr_mixin : public Base {
protected: protected:
typedef enable_weak_ptr combined_type;
template<typename... Args> template<typename... Args>
enable_weak_ptr_mixin(Args&&... args) enable_weak_ptr(Args&&... args)
: super(std::forward<Args>(args)...) : Base(std::forward<Args>(args)...)
, m_anchor(new weak_ptr_anchor(this)) { } , m_anchor(new weak_ptr_anchor(this)) { }
void request_deletion() { void request_deletion() {
...@@ -81,4 +81,4 @@ class enable_weak_ptr_mixin : public Base { ...@@ -81,4 +81,4 @@ class enable_weak_ptr_mixin : public Base {
} // namespace cppa } // namespace cppa
#endif // CPPA_ENABLE_WEAK_PTR_MIXIN_HPP #endif // CPPA_ENABLE_WEAK_PTR_HPP
...@@ -38,23 +38,21 @@ ...@@ -38,23 +38,21 @@
#include "cppa/config.hpp" #include "cppa/config.hpp"
#include "cppa/either.hpp" #include "cppa/either.hpp"
#include "cppa/extend.hpp"
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/detail/receive_policy.hpp" #include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa { namespace cppa {
#ifdef CPPA_DOCUMENTATION
/** /**
* @brief Base class for all event-based actor implementations. * @brief Base class for all event-based actor implementations.
*/ */
class event_based_actor : public scheduled_actor { 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; friend class detail::receive_policy;
typedef detail::abstract_scheduled_actor super;
typedef scheduled_actor super;
public: public:
...@@ -81,12 +79,14 @@ class event_based_actor : public detail::abstract_scheduled_actor { ...@@ -81,12 +79,14 @@ class event_based_actor : public detail::abstract_scheduled_actor {
*/ */
virtual void init() = 0; 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(); bool has_behavior();
scheduled_actor_type impl_type(); scheduled_actor_type impl_type();
static intrusive_ptr<event_based_actor> from(std::function<void()> fun);
protected: protected:
event_based_actor(); event_based_actor();
...@@ -147,7 +147,7 @@ class event_based_actor : public detail::abstract_scheduled_actor { ...@@ -147,7 +147,7 @@ class event_based_actor : public detail::abstract_scheduled_actor {
static const detail::receive_policy_flag receive_flag = detail::rp_sequential; static const detail::receive_policy_flag receive_flag = detail::rp_sequential;
inline void handle_timeout(behavior& bhvr) { inline void handle_timeout(behavior& bhvr) {
CPPA_REQUIRE(bhvr.timeout().valid()); CPPA_REQUIRE(bhvr.timeout().valid());
m_has_pending_timeout_request = false; reset_timeout();
bhvr.handle_timeout(); bhvr.handle_timeout();
if (m_bhvr_stack.empty() == false) { if (m_bhvr_stack.empty() == false) {
request_timeout(get_behavior().timeout()); request_timeout(get_behavior().timeout());
......
...@@ -28,41 +28,45 @@ ...@@ -28,41 +28,45 @@
\******************************************************************************/ \******************************************************************************/
#ifndef CPPA_SCHEDULING_HINT_HPP #ifndef CPPA_MIXED_HPP
#define CPPA_SCHEDULING_HINT_HPP #define CPPA_MIXED_HPP
// saves some typing
#define CPPA_MIXIN template<class,class> class
namespace cppa { namespace cppa {
/** namespace detail {
* @brief Denotes whether a user wants an actor to take part in
* cooperative scheduling or not.
*/
enum scheduling_hint {
/** template<class B, class D, CPPA_MIXIN... Ms>
* @brief Indicates that an actor takes part in cooperative scheduling. struct extend_helper;
*/
scheduled,
/** template<class B, class D>
* @brief Indicates that an actor should run in its own thread. struct extend_helper<B,D> { typedef D type; };
*/
detached,
/** template<class B, class D, CPPA_MIXIN M, CPPA_MIXIN... Ms>
* @brief Indicates that an actor should run in its own thread, struct extend_helper<B,D,M,Ms...> : extend_helper<B,M<D,B>,Ms...> { };
* but it is ignored by {@link await_others_done()}.
*/ } // namespace detail
detached_and_hidden,
/**
* @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 Indicates that an actor takes part in cooperative scheduling, * @brief An alias for the result type.
* but it is ignored by {@link await_others_done()}.
*/ */
scheduled_and_hidden template<template<class,class> class... Mixins>
using with = typename detail::extend_helper<Derived,Base,Mixins...>::type;
}; };
} // namespace cppa } // namespace cppa
#endif // CPPA_SCHEDULING_HINT_HPP #endif // CPPA_MIXED_HPP
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
namespace cppa { namespace factory { namespace cppa { namespace factory {
#ifdef CPPA_DOCUMENTATION void default_cleanup();
/** /**
* @brief Returns a factory for event-based actors using @p fun as * @brief Returns a factory for event-based actors using @p fun as
...@@ -57,7 +57,10 @@ namespace cppa { namespace factory { ...@@ -57,7 +57,10 @@ namespace cppa { namespace factory {
* unless an initial value is passed to @p spawn. * unless an initial value is passed to @p spawn.
*/ */
template<typename InitFun> 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 * @brief Returns a factory for event-based actors using @p fun0 as
...@@ -65,27 +68,12 @@ auto event_based(InitFun fun); ...@@ -65,27 +68,12 @@ auto event_based(InitFun fun);
* and @p fun1 as implementation for * and @p fun1 as implementation for
* {@link cppa::event_based_actor::on_exit() on_exit()}. * {@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> template<typename InitFun, typename OnExitFun>
inline typename detail::ebaf_from_functor<InitFun, OnExitFun>::type inline typename detail::ebaf_from_functor<InitFun, OnExitFun>::type
event_based(InitFun init, OnExitFun on_exit) { event_based(InitFun init, OnExitFun on_exit) {
return {std::move(init), on_exit}; return {std::move(init), on_exit};
} }
#endif // CPPA_DOCUMENTATION
} } // namespace cppa::factory } } // namespace cppa::factory
#endif // CPPA_FACTORY_HPP #endif // CPPA_FACTORY_HPP
...@@ -31,9 +31,6 @@ ...@@ -31,9 +31,6 @@
#ifndef CPPA_GET_HPP #ifndef CPPA_GET_HPP
#define CPPA_GET_HPP #define CPPA_GET_HPP
// functions are documented in the implementation headers
#ifndef CPPA_DOCUMENTATION
#include <tuple> #include <tuple>
#include <cstddef> #include <cstddef>
...@@ -108,5 +105,4 @@ inline auto get_cv_aware(const Tuple& tup) -> decltype(get<Pos>(tup)) { ...@@ -108,5 +105,4 @@ inline auto get_cv_aware(const Tuple& tup) -> decltype(get<Pos>(tup)) {
} // namespace cppa } // namespace cppa
#endif // CPPA_DOCUMENTATION
#endif // CPPA_GET_HPP #endif // CPPA_GET_HPP
...@@ -728,11 +728,6 @@ struct mutable_gref_wrapped<T&> { ...@@ -728,11 +728,6 @@ struct mutable_gref_wrapped<T&> {
namespace placeholders { namespace placeholders {
// doxygen cannot handle anonymous namespaces
#ifndef CPPA_DOCUMENTATION
namespace {
#endif // CPPA_DOCUMENTATION
constexpr guard_placeholder<0> _x1; constexpr guard_placeholder<0> _x1;
constexpr guard_placeholder<1> _x2; constexpr guard_placeholder<1> _x2;
constexpr guard_placeholder<2> _x3; constexpr guard_placeholder<2> _x3;
...@@ -743,11 +738,6 @@ constexpr guard_placeholder<6> _x7; ...@@ -743,11 +738,6 @@ constexpr guard_placeholder<6> _x7;
constexpr guard_placeholder<7> _x8; constexpr guard_placeholder<7> _x8;
constexpr guard_placeholder<8> _x9; constexpr guard_placeholder<8> _x9;
// doxygen cannot handle anonymous namespaces
#ifndef CPPA_DOCUMENTATION
} // namespace <anonymous>
#endif // CPPA_DOCUMENTATION
} // namespace placeholders } // namespace placeholders
} // namespace cppa } // namespace cppa
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "cppa/group.hpp" #include "cppa/group.hpp"
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
#include "cppa/extend.hpp"
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/message_id.hpp" #include "cppa/message_id.hpp"
...@@ -43,9 +44,9 @@ ...@@ -43,9 +44,9 @@
#include "cppa/exit_reason.hpp" #include "cppa/exit_reason.hpp"
#include "cppa/response_handle.hpp" #include "cppa/response_handle.hpp"
#include "cppa/partial_function.hpp" #include "cppa/partial_function.hpp"
#include "cppa/memory_cached_mixin.hpp"
#include "cppa/util/duration.hpp" #include "cppa/util/duration.hpp"
#include "cppa/memory_cached.hpp"
#include "cppa/detail/behavior_stack.hpp" #include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/recursive_queue_node.hpp" #include "cppa/detail/recursive_queue_node.hpp"
...@@ -60,45 +61,33 @@ class sync_handle_helper; ...@@ -60,45 +61,33 @@ class sync_handle_helper;
namespace detail { class receive_policy; } namespace detail { class receive_policy; }
template<bool DiscardOld> template<bool DiscardBehavior>
struct behavior_policy { static const bool discard_old = DiscardOld; }; struct behavior_policy { static constexpr bool discard_old = DiscardBehavior; };
typedef behavior_policy<false> keep_behavior_t; typedef behavior_policy<false> keep_behavior_t;
typedef behavior_policy<true > discard_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 * @brief Policy tag that causes {@link event_based_actor::become} to
* discard the current behavior. * discard the current behavior.
* @relates local_actor * @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 * @brief Policy tag that causes {@link event_based_actor::become} to
* keep the current behavior available. * keep the current behavior available.
* @relates local_actor * @relates local_actor
*/ */
constexpr keep_behavior_t keep_behavior = keep_behavior_t(); 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);
/** /**
* @brief Base class for local running Actors. * @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 scheduler;
friend class detail::memory; friend class detail::memory;
...@@ -163,39 +152,29 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -163,39 +152,29 @@ class local_actor : public memory_cached_mixin<actor> {
/** /**
* @brief Checks whether this actor traps exit messages. * @brief Checks whether this actor traps exit messages.
*/ */
inline bool trap_exit() const { inline bool trap_exit() const;
return m_trap_exit;
}
/** /**
* @brief Enables or disables trapping of exit messages. * @brief Enables or disables trapping of exit messages.
*/ */
inline void trap_exit(bool new_value) { inline void trap_exit(bool new_value);
m_trap_exit = new_value;
}
/** /**
* @brief Checks whether this actor uses the "chained send" optimization. * @brief Checks whether this actor uses the "chained send" optimization.
*/ */
inline bool chaining() const { inline bool chaining() const;
return m_chaining;
}
/** /**
* @brief Enables or disables chained send. * @brief Enables or disables chained send.
*/ */
inline void chaining(bool new_value) { inline void chaining(bool new_value);
m_chaining = m_is_scheduled && new_value;
}
/** /**
* @brief Returns the last message that was dequeued * @brief Returns the last message that was dequeued
* from the actor's mailbox. * from the actor's mailbox.
* @warning Only set during callback invocation. * @warning Only set during callback invocation.
*/ */
inline any_tuple& last_dequeued() { inline any_tuple& last_dequeued();
return m_current_node->msg;
}
/** /**
* @brief Returns the sender of the last dequeued message. * @brief Returns the sender of the last dequeued message.
...@@ -203,9 +182,7 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -203,9 +182,7 @@ class local_actor : public memory_cached_mixin<actor> {
* @note Implicitly used by the function {@link cppa::reply}. * @note Implicitly used by the function {@link cppa::reply}.
* @see cppa::reply() * @see cppa::reply()
*/ */
inline actor_ptr& last_sender() { inline actor_ptr& last_sender();
return m_current_node->sender;
}
/** /**
* @brief Adds a unidirectional @p monitor to @p whom. * @brief Adds a unidirectional @p monitor to @p whom.
...@@ -224,74 +201,37 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -224,74 +201,37 @@ class local_actor : public memory_cached_mixin<actor> {
// become/unbecome API // 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 * @brief Sets the actor's behavior to @p bhvr and discards the
* previous behavior if the policy is {@link discard_behavior}. * previous behavior if the policy is {@link discard_behavior}.
*/ */
template<bool DiscardOld> template<bool Discard>
inline void become(behavior_policy<DiscardOld>, behavior bhvr) { inline void become(behavior_policy<Discard>, behavior bhvr);
do_become(std::move(bhvr), true);
}
/** /**
* @brief Sets the actor's behavior and discards the * @brief Sets the actor's behavior and discards the
* previous behavior if the policy is {@link discard_behavior}. * previous behavior if the policy is {@link discard_behavior}.
*/ */
template<bool DiscardOld, typename Arg0, typename Arg1, typename... Args> template<bool Discard, typename T0, typename T1, typename... Ts>
inline void become(behavior_policy<DiscardOld>, inline void become(behavior_policy<Discard>, T0&& a0, T1&& a1, Ts&&... as);
Arg0&& arg0, Arg1&& arg1, Args&&... args) {
do_become(match_expr_convert(std::forward<Arg0>(arg0),
std::forward<Arg1>(arg1),
std::forward<Args>(args)...),
DiscardOld);
}
/**
* @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));
}
/** /**
* @brief Sets the actor's behavior; * @brief Sets the actor's behavior;
* equal to <tt>become(discard_old, bhvr</tt>. * equal to <tt>become(discard_old, bhvr</tt>.
*/ */
inline void become(behavior* bhvr) { inline void become(behavior bhvr);
become(discard_behavior, bhvr);
}
/** /**
* @brief Sets the actor's behavior; * @brief Sets the actor's behavior;
* equal to <tt>become(discard_old, bhvr</tt>. * equal to <tt>become(discard_old, bhvr</tt>.
*/ */
template<typename... Cases, typename... Args> template<typename... Cases, typename... Ts>
inline void become(match_expr<Cases...> arg0, Args&&... args) { inline void become(match_expr<Cases...> a, Ts&&... as);
do_become(match_expr_convert(std::move(arg0),
std::forward<Args>(args)...),
true);
}
/** /**
* @brief Returns to a previous behavior if available. * @brief Returns to a previous behavior if available.
*/ */
inline void unbecome() { inline void unbecome();
m_bhvr_stack.pop_async_back();
}
/** /**
* @brief Can be overridden to initialize an actor before any * @brief Can be overridden to initialize an actor before any
...@@ -379,7 +319,7 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -379,7 +319,7 @@ class local_actor : public memory_cached_mixin<actor> {
local_actor(bool is_scheduled = false); local_actor(bool is_scheduled = false);
virtual bool initialized() = 0; virtual bool initialized() const = 0;
inline bool chaining_enabled() { inline bool chaining_enabled() {
return m_chaining && !m_chained_actor; return m_chaining && !m_chained_actor;
...@@ -389,7 +329,7 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -389,7 +329,7 @@ class local_actor : public memory_cached_mixin<actor> {
whom->enqueue(this, std::move(what)); 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 (chaining_enabled()) {
if (whom->chained_enqueue(this, std::move(what))) { if (whom->chained_enqueue(this, std::move(what))) {
m_chained_actor = whom; m_chained_actor = whom;
...@@ -398,7 +338,7 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -398,7 +338,7 @@ class local_actor : public memory_cached_mixin<actor> {
else whom->enqueue(this, std::move(what)); 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; auto id = ++m_last_request_id;
CPPA_REQUIRE(id.is_request()); CPPA_REQUIRE(id.is_request());
if (chaining_enabled()) { if (chaining_enabled()) {
...@@ -412,7 +352,7 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -412,7 +352,7 @@ class local_actor : public memory_cached_mixin<actor> {
return awaited_response; 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, const util::duration& rel_time,
any_tuple&& what); any_tuple&& what);
...@@ -491,6 +431,8 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -491,6 +431,8 @@ class local_actor : public memory_cached_mixin<actor> {
m_bhvr_stack.erase(id); m_bhvr_stack.erase(id);
} }
void cleanup(std::uint32_t reason);
private: private:
std::function<void()> m_sync_failure_handler; std::function<void()> m_sync_failure_handler;
...@@ -515,6 +457,58 @@ class local_actor : public memory_cached_mixin<actor> { ...@@ -515,6 +457,58 @@ class local_actor : public memory_cached_mixin<actor> {
*/ */
typedef intrusive_ptr<local_actor> local_actor_ptr; 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 } // namespace cppa
#endif // CPPA_CONTEXT_HPP #endif // CPPA_CONTEXT_HPP
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <sstream> #include <sstream>
#include "cppa/singletons.hpp"
#include "cppa/detail/demangle.hpp" #include "cppa/detail/demangle.hpp"
/* /*
...@@ -65,27 +66,17 @@ class logging { ...@@ -65,27 +66,17 @@ class logging {
int line_num, int line_num,
const std::string& msg ) = 0; const std::string& msg ) = 0;
static logging* instance();
class trace_helper { class trace_helper {
public: public:
inline trace_helper(std::string class_name, trace_helper(std::string class_name,
const char* fun_name, const char* fun_name,
const char* file_name, const char* file_name,
int line_num, int line_num,
const std::string& msg) 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) { ~trace_helper();
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");
}
private: private:
...@@ -115,129 +106,119 @@ class logging { ...@@ -115,129 +106,119 @@ class logging {
#define CPPA_VOID_STMT static_cast<void>(0) #define CPPA_VOID_STMT static_cast<void>(0)
#ifndef CPPA_LOG_LEVEL #ifndef CPPA_LOG_LEVEL
# define CPPA_LOG(classname, funname, level, message) CPPA_VOID_STMT
#define CPPA_LOG_ERROR(unused) CPPA_VOID_STMT # define CPPA_LIF(stmt, logstmt) 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
#else #else
# define CPPA_LOG(classname, funname, level, message) { \
#define CPPA_DO_LOG_FUN(level, message) { \
std::ostringstream scoped_oss; scoped_oss << message; \ std::ostringstream scoped_oss; scoped_oss << message; \
::cppa::logging::instance()->log( \ ::cppa::get_logger()->log( \
level, "NONE", \ level, classname, funname, __FILE__, __LINE__, scoped_oss.str()); \
__FUNCTION__, __FILE__, __LINE__, scoped_oss.str()); \ } CPPA_VOID_STMT
} CPPA_VOID_STMT # define CPPA_LIF(stmt, logstmt) if (stmt) { logstmt ; } CPPA_VOID_STMT
#endif
#define CPPA_DO_LOG_MEMBER_FUN(level, message) { \ #define CPPA_CLASS_NAME ::cppa::detail::demangle(typeid(*this)).c_str()
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_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 #if CPPA_LOG_LEVEL > 0
# define CPPA_LOG_WARNING(message) CPPA_DO_LOG_MEMBER_FUN("WARNING", message) # undef CPPA_LOGC_WARNING
# define CPPA_LOGF_WARNING(message) CPPA_DO_LOG_FUN("WARNING", message) # define CPPA_LOGC_WARNING(cname, fname, msg) CPPA_LOG(cname, fname, "WARN ", msg)
#else
# define CPPA_LOG_WARNING(unused) CPPA_VOID_STMT
# define CPPA_LOGF_WARNING(unused) CPPA_VOID_STMT
#endif #endif
// enable info messages
#if CPPA_LOG_LEVEL > 1 #if CPPA_LOG_LEVEL > 1
# define CPPA_LOG_INFO(message) CPPA_DO_LOG_MEMBER_FUN("INFO ", message) # undef CPPA_LOGC_INFO
# define CPPA_LOGF_INFO(message) CPPA_DO_LOG_FUN("INFO ", message) # define CPPA_LOGC_INFO(cname, fname, msg) CPPA_LOG(cname, fname, "INFO ", msg)
#else
# define CPPA_LOG_INFO(unused) CPPA_VOID_STMT
# define CPPA_LOGF_INFO(unused) CPPA_VOID_STMT
#endif #endif
// enable debug messages
#if CPPA_LOG_LEVEL > 2 #if CPPA_LOG_LEVEL > 2
# define CPPA_LOG_DEBUG(message) CPPA_DO_LOG_MEMBER_FUN("DEBUG ", message) # undef CPPA_LOGC_DEBUG
# define CPPA_LOGF_DEBUG(message) CPPA_DO_LOG_FUN("DEBUG ", message) # define CPPA_LOGC_DEBUG(cname, fname, msg) CPPA_LOG(cname, fname, "DEBUG", msg)
#else
# define CPPA_LOG_DEBUG(unused) CPPA_VOID_STMT
# define CPPA_LOGF_DEBUG(unused) CPPA_VOID_STMT
#endif #endif
// enable trace messages
#if CPPA_LOG_LEVEL > 3 #if CPPA_LOG_LEVEL > 3
# define CPPA_RPAREN ) # undef CPPA_LOGC_TRACE
# define CPPA_LPAREN (
# define CPPA_GET(what) what
# define CPPA_CONCAT_I(lhs,rhs) lhs ## rhs # define CPPA_CONCAT_I(lhs,rhs) lhs ## rhs
# define CPPA_CONCAT(lhs,rhs) CPPA_CONCAT_I(lhs,rhs) # define CPPA_CONCAT(lhs,rhs) CPPA_CONCAT_I(lhs,rhs)
# define CPPA_CONCATL(lhs) CPPA_CONCAT(lhs, __LINE__) # define CPPA_CONCATL(lhs) CPPA_CONCAT(lhs, __LINE__)
# define CPPA_LOG_TRACE(message) \ # define CPPA_LOGC_TRACE(cname, fname, msg) \
::std::ostringstream CPPA_CONCATL(cppa_trace_helper_) ; \ ::std::ostringstream CPPA_CONCATL(cppa_trace_helper_) ; \
CPPA_CONCATL(cppa_trace_helper_) << message ; \ CPPA_CONCATL(cppa_trace_helper_) << msg ; \
::cppa::logging::trace_helper CPPA_CONCATL(cppa_fun_trace_helper_) \ ::cppa::logging::trace_helper CPPA_CONCATL(cppa_fun_trace_helper_) { \
CPPA_LPAREN ::cppa::detail::demangle \ cname, fname , __FILE__ , __LINE__ , \
CPPA_LPAREN typeid CPPA_LPAREN decltype CPPA_LPAREN *this \ CPPA_CONCATL(cppa_trace_helper_) .str() }
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)
#endif #endif
#define CPPA_LOG_ERROR_IF(stmt,message) \ /**
if (stmt) { CPPA_LOG_ERROR(message); }; CPPA_VOID_STMT * @brief Logs @p msg with custom member function @p fname.
*/
#define CPPA_LOG_WARNING_IF(stmt,message) \ #define CPPA_LOGS_ERROR(fname, msg) CPPA_LOGC_ERROR(CPPA_CLASS_NAME, fname, msg)
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
#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_ARG(arg) #arg << " = " << arg
#define CPPA_TARG(arg, trans) #arg << " = " << trans ( arg ) #define CPPA_TARG(arg, trans) #arg << " = " << trans ( arg )
#define CPPA_MARG(arg, memfun) #arg << " = " << arg . memfun () #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 @@ ...@@ -41,6 +41,7 @@
#include "cppa/util/rm_option.hpp" #include "cppa/util/rm_option.hpp"
#include "cppa/util/purge_refs.hpp" #include "cppa/util/purge_refs.hpp"
#include "cppa/util/apply_args.hpp" #include "cppa/util/apply_args.hpp"
#include "cppa/util/disjunction.hpp"
#include "cppa/util/left_or_right.hpp" #include "cppa/util/left_or_right.hpp"
#include "cppa/util/deduce_ref_type.hpp" #include "cppa/util/deduce_ref_type.hpp"
...@@ -86,7 +87,7 @@ struct invoke_policy_impl : invoke_policy_base<FilteredPattern> { ...@@ -86,7 +87,7 @@ struct invoke_policy_impl : invoke_policy_base<FilteredPattern> {
template<typename PtrType, class Tuple> template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result, static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& type_token, const std::type_info& type_token,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple& tup) { Tuple& tup) {
typedef typename match_impl_from_type_list< typedef typename match_impl_from_type_list<
...@@ -118,7 +119,7 @@ struct invoke_policy_impl<wildcard_position::nil, ...@@ -118,7 +119,7 @@ struct invoke_policy_impl<wildcard_position::nil,
template<typename PtrType, class Tuple> template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type&, static bool prepare_invoke(typename super::tuple_type&,
const std::type_info& type_token, const std::type_info& type_token,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple& tup) { Tuple& tup) {
return can_invoke(type_token, tup); return can_invoke(type_token, tup);
...@@ -161,7 +162,7 @@ struct invoke_policy_impl<wildcard_position::nil, ...@@ -161,7 +162,7 @@ struct invoke_policy_impl<wildcard_position::nil,
template<typename PtrType, class Tuple> template<typename PtrType, class Tuple>
static bool prepare_invoke(tuple_type& result, static bool prepare_invoke(tuple_type& result,
const std::type_info&, const std::type_info&,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple& tup, Tuple& tup,
typename std::enable_if< typename std::enable_if<
...@@ -185,7 +186,7 @@ struct invoke_policy_impl<wildcard_position::nil, ...@@ -185,7 +186,7 @@ struct invoke_policy_impl<wildcard_position::nil,
template<typename PtrType, class Tuple> template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result, static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types, const std::type_info& arg_types,
detail::tuple_impl_info timpl, bool dynamically_typed,
PtrType* native_arg, PtrType* native_arg,
Tuple& tup, Tuple& tup,
typename std::enable_if< typename std::enable_if<
...@@ -210,7 +211,7 @@ struct invoke_policy_impl<wildcard_position::nil, ...@@ -210,7 +211,7 @@ struct invoke_policy_impl<wildcard_position::nil,
} }
// 'fall through' // 'fall through'
} }
else if (timpl == detail::dynamically_typed) { else if (dynamically_typed) {
auto& arr = arr_type::arr; auto& arr = arr_type::arr;
if (tup.size() != sizeof...(Ts)) { if (tup.size() != sizeof...(Ts)) {
return false; return false;
...@@ -252,7 +253,7 @@ struct invoke_policy_impl<wildcard_position::leading, ...@@ -252,7 +253,7 @@ struct invoke_policy_impl<wildcard_position::leading,
template<typename PtrType, typename Tuple> template<typename PtrType, typename Tuple>
static inline bool prepare_invoke(typename super::tuple_type&, static inline bool prepare_invoke(typename super::tuple_type&,
const std::type_info&, const std::type_info&,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple&) { Tuple&) {
return true; return true;
...@@ -290,7 +291,7 @@ struct invoke_policy_impl<wildcard_position::trailing, ...@@ -290,7 +291,7 @@ struct invoke_policy_impl<wildcard_position::trailing,
template<typename PtrType, class Tuple> template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result, static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types, const std::type_info& arg_types,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple& tup) { Tuple& tup) {
if (!can_invoke(arg_types, tup)) return false; if (!can_invoke(arg_types, tup)) return false;
...@@ -330,7 +331,7 @@ struct invoke_policy_impl<wildcard_position::leading, ...@@ -330,7 +331,7 @@ struct invoke_policy_impl<wildcard_position::leading,
template<typename PtrType, class Tuple> template<typename PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result, static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types, const std::type_info& arg_types,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple& tup) { Tuple& tup) {
if (!can_invoke(arg_types, tup)) return false; if (!can_invoke(arg_types, tup)) return false;
...@@ -486,7 +487,7 @@ inline bool unroll_expr(PPFPs&, ...@@ -486,7 +487,7 @@ inline bool unroll_expr(PPFPs&,
std::uint64_t, std::uint64_t,
minus1l, minus1l,
const std::type_info&, const std::type_info&,
detail::tuple_impl_info, bool,
PtrType*, PtrType*,
Tuple&) { Tuple&) {
return false; return false;
...@@ -498,11 +499,11 @@ bool unroll_expr(PPFPs& fs, ...@@ -498,11 +499,11 @@ bool unroll_expr(PPFPs& fs,
std::uint64_t bitmask, std::uint64_t bitmask,
long_constant<N>, long_constant<N>,
const std::type_info& type_token, const std::type_info& type_token,
detail::tuple_impl_info iinfo, bool is_dynamic,
PtrType* ptr, PtrType* ptr,
Tuple& tup) { Tuple& tup) {
if (unroll_expr(fs, invoke_res, bitmask, long_constant<N-1>{}, if (unroll_expr(fs, invoke_res, bitmask, long_constant<N-1>{},
type_token, iinfo, ptr, tup)) { type_token, is_dynamic, ptr, tup)) {
return true; return true;
} }
if ((bitmask & (0x01 << N)) == 0) return false; if ((bitmask & (0x01 << N)) == 0) return false;
...@@ -511,7 +512,7 @@ bool unroll_expr(PPFPs& fs, ...@@ -511,7 +512,7 @@ bool unroll_expr(PPFPs& fs,
typedef typename Fun::pattern_type pattern_type; typedef typename Fun::pattern_type pattern_type;
typedef detail::invoke_policy<pattern_type> policy; typedef detail::invoke_policy<pattern_type> policy;
typename policy::tuple_type targs; 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); auto is = util::get_indices(targs);
util::void_type dummy; util::void_type dummy;
typename has_bool_result<typename Fun::second_type>::token_type stoken; typename has_bool_result<typename Fun::second_type>::token_type stoken;
...@@ -626,6 +627,8 @@ class match_expr { ...@@ -626,6 +627,8 @@ class match_expr {
public: public:
static constexpr bool may_have_timeout = false;
typedef util::type_list<Cases...> cases_list; typedef util::type_list<Cases...> cases_list;
static constexpr size_t num_cases = sizeof...(Cases); static constexpr size_t num_cases = sizeof...(Cases);
...@@ -666,7 +669,7 @@ class match_expr { ...@@ -666,7 +669,7 @@ class match_expr {
bool can_invoke(const any_tuple& tup) { bool can_invoke(const any_tuple& tup) {
auto type_token = tup.type_token(); 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); auto bitmask = get_cache_entry(type_token, tup);
return bitmask != 0; return bitmask != 0;
} }
...@@ -713,7 +716,6 @@ class match_expr { ...@@ -713,7 +716,6 @@ class match_expr {
>::type >::type
ptr_type; ptr_type;
auto impl_type = detail::statically_typed;
ptr_type ptr_arg = nullptr; ptr_type ptr_arg = nullptr;
bool invoke_result = true; bool invoke_result = true;
bool unroll_result = unroll_expr(m_cases, bool unroll_result = unroll_expr(m_cases,
...@@ -721,7 +723,7 @@ class match_expr { ...@@ -721,7 +723,7 @@ class match_expr {
bitmask, bitmask,
idx_token, idx_token,
type_token, type_token,
impl_type, false, // not dynamically_typed
ptr_arg, ptr_arg,
static_cast<ref_type>(tup)); static_cast<ref_type>(tup));
return unroll_result && invoke_result; return unroll_result && invoke_result;
...@@ -797,7 +799,7 @@ class match_expr { ...@@ -797,7 +799,7 @@ class match_expr {
std::uint64_t get_cache_entry(const std::type_info* type_token, std::uint64_t get_cache_entry(const std::type_info* type_token,
const Tuple& value) { const Tuple& value) {
CPPA_REQUIRE(type_token != nullptr); CPPA_REQUIRE(type_token != nullptr);
if (value.impl_type() == detail::dynamically_typed) { if (value.dynamically_typed()) {
return m_dummy.second; // all groups enabled return m_dummy.second; // all groups enabled
} }
size_t i = find_token_pos(type_token); size_t i = find_token_pos(type_token);
...@@ -831,14 +833,14 @@ class match_expr { ...@@ -831,14 +833,14 @@ class match_expr {
auto ndp = fetch_native_data(vals, mutator_token); auto ndp = fetch_native_data(vals, mutator_token);
auto token_ptr = vals->type_token(); auto token_ptr = vals->type_token();
auto bitmask = get_cache_entry(token_ptr, *vals); 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 invoke_result = true; // maybe set to false by an invoked functor
bool unroll_result = unroll_expr(m_cases, bool unroll_result = unroll_expr(m_cases,
invoke_result, invoke_result,
bitmask, bitmask,
idx_token, idx_token,
*token_ptr, *token_ptr,
impl_type, dynamically_typed,
ndp, ndp,
*vals); *vals);
return invoke_result && unroll_result; return invoke_result && unroll_result;
...@@ -889,53 +891,29 @@ match_expr_collect(const Arg0& arg0, const Args&... args) { ...@@ -889,53 +891,29 @@ match_expr_collect(const Arg0& arg0, const Args&... args) {
return {all_cases}; return {all_cases};
} }
template<bool HasTimeout> namespace detail {
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<
typename util::tl_map<
typename util::tl_concat<
typename Arg0::cases_list,
typename Args::cases_list...
>::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<> typedef std::true_type with_timeout;
struct match_expr_concat_impl<true> { typedef std::false_type without_timeout;
template<class TData, class Token, typename F> // with timeout
static detail::behavior_impl* __(const TData& data, Token, const timeout_definition<F>& arg0) {
// 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; typedef typename match_expr_from_type_list<Token>::type combined_type;
typedef detail::default_behavior_impl<combined_type,F> impl_type; return new default_behavior_impl<combined_type,F>{data, arg};
return new impl_type(data, arg0); }
}
template<class TData, class Token, typename... Cases, typename... Args> // recursive concatenation function
static detail::behavior_impl* __(const TData& data, Token, const match_expr<Cases...>& arg0, const Args&... args) { 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< typedef typename util::tl_concat<
Token, Token,
util::type_list<Cases...> typename T::cases_list
>::type >::type
next_token_type; next_token_type;
typename detail::tdata_from_type_list< typename tdata_from_type_list<
typename util::tl_map< typename util::tl_map<
next_token_type, next_token_type,
gref_wrapped gref_wrapped
...@@ -943,41 +921,65 @@ struct match_expr_concat_impl<true> { ...@@ -943,41 +921,65 @@ struct match_expr_concat_impl<true> {
>::type >::type
next_data; next_data;
next_token_type next_token; next_token_type next_token;
detail::collect_tdata(next_data, data, arg0.cases()); collect_tdata(next_data, data, arg.cases());
return __(next_data, next_token, args...); return concat_rec(next_data, next_token, args...);
} }
template<typename F> template<typename F>
static detail::behavior_impl* _(const timeout_definition<F>& arg0) { behavior_impl* concat_expr(with_timeout, const timeout_definition<F>& arg) {
typedef detail::default_behavior_impl<detail::dummy_match_expr,F> impl_type; typedef default_behavior_impl<dummy_match_expr,F> impl_type;
return new impl_type(detail::dummy_match_expr{}, arg0); return new impl_type(dummy_match_expr{}, arg);
} }
template<typename... Cases, typename... Args> template<typename T, typename... Ts>
static detail::behavior_impl* _(const match_expr<Cases...>& arg0, const Args&... args) { behavior_impl* concat_expr(with_timeout, const T& arg, const Ts&... args) {
util::type_list<Cases...> token; typename tdata_from_type_list<
typename detail::tdata_from_type_list<
typename util::tl_map< typename util::tl_map<
util::type_list<Cases...>, typename T::cases_list,
gref_wrapped gref_wrapped
>::type >::type
>::type >::type
wrapper; wrapper;
detail::collect_tdata(wrapper, arg0.cases()); detail::collect_tdata(wrapper, arg.cases());
return __(wrapper, token, args...); return concat_rec(wrapper, typename T::cases_list{}, args...);
} }
}; // without timeout
template<typename Arg0, typename... Args> template<typename T, typename... Ts>
intrusive_ptr<detail::behavior_impl> match_expr_concat(const Arg0& arg0, behavior_impl* concat_expr(without_timeout, const T& arg, const Ts&... args) {
const Args&... args) { typename tdata_from_type_list<
constexpr bool has_timeout = util::disjunction< typename util::tl_map<
is_timeout_definition<Arg0>, typename util::tl_concat<
is_timeout_definition<Args>...>::value; typename T::cases_list,
return {match_expr_concat_impl<has_timeout>::_(arg0, args...)}; 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
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 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 } // namespace cppa
#endif // CPPA_MATCH_EXPR_HPP #endif // CPPA_MATCH_EXPR_HPP
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
\******************************************************************************/ \******************************************************************************/
#ifndef MEMORY_CACHED_MIXIN_HPP #ifndef CPPA_MEMORY_CACHED_HPP
#define MEMORY_CACHED_MIXIN_HPP #define CPPA_MEMORY_CACHED_HPP
#include "cppa/detail/memory.hpp" #include "cppa/detail/memory.hpp"
...@@ -39,19 +39,21 @@ namespace cppa { ...@@ -39,19 +39,21 @@ namespace cppa {
* @brief This mixin adds all member functions and member variables needed * @brief This mixin adds all member functions and member variables needed
* by the memory management subsystem. * by the memory management subsystem.
*/ */
template<typename Base> template<class Base, class Subtype>
class memory_cached_mixin : public Base { class memory_cached : public Base {
friend class detail::memory; friend class detail::memory;
template<typename T> template<typename>
friend class detail::basic_memory_cache; friend class detail::basic_memory_cache;
protected: protected:
template<typename... Args> typedef memory_cached combined_type;
memory_cached_mixin(Args&&... args)
: Base(std::forward<Args>(args)...), outer_memory(nullptr) { } template<typename... Ts>
memory_cached(Ts&&... args) : Base(std::forward<Ts>(args)...)
, outer_memory(nullptr) { }
virtual void request_deletion() { virtual void request_deletion() {
auto mc = detail::memory::get_cache_map_entry(&typeid(*this)); auto mc = detail::memory::get_cache_map_entry(&typeid(*this));
...@@ -74,4 +76,4 @@ class memory_cached_mixin : public Base { ...@@ -74,4 +76,4 @@ class memory_cached_mixin : public Base {
} // namespace cppa } // namespace cppa
#endif // MEMORY_CACHED_MIXIN_HPP #endif // CPPA_MEMORY_CACHED_HPP
...@@ -31,9 +31,11 @@ ...@@ -31,9 +31,11 @@
#ifndef MESSAGE_FUTURE_HPP #ifndef MESSAGE_FUTURE_HPP
#define MESSAGE_FUTURE_HPP #define MESSAGE_FUTURE_HPP
#include <cstdint>
#include <type_traits> #include <type_traits>
#include "cppa/on.hpp" #include "cppa/on.hpp"
#include "cppa/atom.hpp"
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/match_expr.hpp" #include "cppa/match_expr.hpp"
#include "cppa/message_id.hpp" #include "cppa/message_id.hpp"
...@@ -130,12 +132,8 @@ class message_future { ...@@ -130,12 +132,8 @@ class message_future {
message_future(const message_future&) = default; message_future(const message_future&) = default;
message_future& operator=(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) { } inline message_future(const message_id_t& from) : m_mid(from) { }
# endif
private: private:
message_id_t m_mid; message_id_t m_mid;
...@@ -147,7 +145,7 @@ class message_future { ...@@ -147,7 +145,7 @@ class message_future {
return false; return false;
}; };
return { 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("VOID")) >> skip_message,
on(atom("TIMEOUT")) >> handle_sync_timeout, on(atom("TIMEOUT")) >> handle_sync_timeout,
(on(any_vals, arg_match) >> fs)... (on(any_vals, arg_match) >> fs)...
......
...@@ -31,12 +31,11 @@ ...@@ -31,12 +31,11 @@
#ifndef DEFAULT_ACTOR_PROXY_HPP #ifndef DEFAULT_ACTOR_PROXY_HPP
#define DEFAULT_ACTOR_PROXY_HPP #define DEFAULT_ACTOR_PROXY_HPP
#include "cppa/extend.hpp"
#include "cppa/actor_proxy.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/network/default_protocol.hpp"
#include "cppa/intrusive/single_reader_queue.hpp"
#include "cppa/detail/abstract_actor.hpp"
namespace cppa { namespace detail { namespace cppa { namespace detail {
...@@ -49,7 +48,8 @@ class basic_memory_cache; ...@@ -49,7 +48,8 @@ class basic_memory_cache;
namespace cppa { namespace network { 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; friend class detail::memory;
...@@ -67,9 +67,9 @@ class sync_request_info : public memory_cached_mixin<memory_managed> { ...@@ -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: public:
...@@ -77,19 +77,19 @@ class default_actor_proxy : public detail::abstract_actor<actor_proxy> { ...@@ -77,19 +77,19 @@ class default_actor_proxy : public detail::abstract_actor<actor_proxy> {
const process_information_ptr& pinfo, const process_information_ptr& pinfo,
const default_protocol_ptr& parent); 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); 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 ...@@ -223,6 +223,11 @@ struct pattern_type : pattern_type_<util::is_callable<T>::value && !detail::is_b
namespace cppa { namespace cppa {
/**
* @brief A wildcard that matches any number of any values.
*/
constexpr anything any_vals = anything{};
#ifdef CPPA_DOCUMENTATION #ifdef CPPA_DOCUMENTATION
/** /**
...@@ -251,12 +256,6 @@ constexpr __unspecified__ skip_message; ...@@ -251,12 +256,6 @@ constexpr __unspecified__ skip_message;
template<typename T> template<typename T>
__unspecified__ val(); __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. * @brief Left-hand side of a partial function expression that matches values.
* *
...@@ -294,8 +293,6 @@ constexpr typename detail::boxed<T>::type val() { ...@@ -294,8 +293,6 @@ constexpr typename detail::boxed<T>::type val() {
return typename detail::boxed<T>::type(); return typename detail::boxed<T>::type();
} }
constexpr anything any_vals = anything();
typedef typename detail::boxed<util::arg_match_t>::type boxed_arg_match_t; typedef typename detail::boxed<util::arg_match_t>::type boxed_arg_match_t;
constexpr boxed_arg_match_t arg_match = 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 { ...@@ -414,7 +411,7 @@ class on_the_fly_rvalue_builder {
constexpr detail::on_the_fly_rvalue_builder on_arg_match; constexpr detail::on_the_fly_rvalue_builder on_arg_match;
#endif #endif // CPPA_DOCUMENTATION
} // namespace cppa } // namespace cppa
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <type_traits>
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
...@@ -62,6 +63,8 @@ class partial_function { ...@@ -62,6 +63,8 @@ class partial_function {
typedef intrusive_ptr<detail::behavior_impl> impl_ptr; typedef intrusive_ptr<detail::behavior_impl> impl_ptr;
static constexpr bool may_have_timeout = false;
partial_function() = default; partial_function() = default;
partial_function(partial_function&&) = default; partial_function(partial_function&&) = default;
partial_function(const partial_function&) = default; partial_function(const partial_function&) = default;
...@@ -95,28 +98,23 @@ class partial_function { ...@@ -95,28 +98,23 @@ class partial_function {
return behavior{m_impl->or_else(other.m_impl)}; return behavior{m_impl->or_else(other.m_impl)};
} }
template<typename F> template<typename T>
inline behavior or_else(const timeout_definition<F>& tdef) const { typename std::conditional<T::may_have_timeout,behavior,partial_function>::type
generic_timeout_definition gtd{tdef.timeout, tdef.handler}; or_else(const T& arg) const;
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> template<typename T0, typename T1, typename... Ts>
typename util::if_else< typename std::conditional<
util::disjunction< util::disjunction<
is_timeout_definition<Arg0>, T0::may_have_timeout,
is_timeout_definition<Arg1>, T1::may_have_timeout,
is_timeout_definition<Args>...>, Ts::may_have_timeout...
>::value,
behavior, behavior,
util::wrapped<partial_function> >::type partial_function
or_else(const Arg0& arg0, const Arg1& arg1, const Args&... args) const { >::type
return m_impl->or_else(match_expr_concat(arg0, arg1, args...)); or_else(const T0& arg0, const T1& arg1, const Ts&... args) const;
}
inline const impl_ptr& as_behavior_impl() const;
private: private:
...@@ -124,15 +122,45 @@ class partial_function { ...@@ -124,15 +122,45 @@ class partial_function {
}; };
template<typename Arg0, typename... Args> template<typename T, typename... Ts>
typename util::if_else< 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< util::disjunction<
is_timeout_definition<Arg0>, T0::may_have_timeout,
is_timeout_definition<Args>...>, T1::may_have_timeout,
Ts::may_have_timeout...
>::value,
behavior, behavior,
util::wrapped<partial_function> >::type partial_function
match_expr_convert(const Arg0& arg0, const Args&... args) { >::type
return {match_expr_concat(arg0, args...)}; 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 } // namespace cppa
......
...@@ -293,8 +293,6 @@ T& get_ref(primitive_variant& pv) { ...@@ -293,8 +293,6 @@ T& get_ref(primitive_variant& pv) {
return pv.get_as<ptype>(); return pv.get_as<ptype>();
} }
#ifdef CPPA_DOCUMENTATION
/** /**
* @ingroup TypeSystem * @ingroup TypeSystem
* @brief Casts a primitive variant to its C++ type. * @brief Casts a primitive variant to its C++ type.
...@@ -304,7 +302,11 @@ T& get_ref(primitive_variant& pv) { ...@@ -304,7 +302,11 @@ T& get_ref(primitive_variant& pv) {
* @returns A const reference to the value of @p pv of type @p T. * @returns A const reference to the value of @p pv of type @p T.
*/ */
template<primitive_type PT> 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 * @ingroup TypeSystem
...@@ -314,18 +316,6 @@ const T& get_ref(const primitive_variant& pv); ...@@ -314,18 +316,6 @@ const T& get_ref(const primitive_variant& pv);
* @param pv A primitive variant of type @p T. * @param pv A primitive variant of type @p T.
* @returns A reference to the value of @p pv 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> template<primitive_type PT>
inline typename detail::ptype_to_type<PT>::type& inline typename detail::ptype_to_type<PT>::type&
get_ref(primitive_variant& pv) { get_ref(primitive_variant& pv) {
...@@ -333,8 +323,6 @@ 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); return get_ref<typename detail::ptype_to_type<PT>::type>(pv);
} }
#endif
/** /**
* @relates primitive_variant * @relates primitive_variant
*/ */
......
...@@ -50,6 +50,10 @@ class sb_actor : public Base { ...@@ -50,6 +50,10 @@ class sb_actor : public Base {
static_assert(std::is_base_of<event_based_actor,Base>::value, static_assert(std::is_base_of<event_based_actor,Base>::value,
"Base must be either event_based_actor or a derived type"); "Base must be either event_based_actor or a derived type");
protected:
typedef sb_actor combined_type;
public: public:
/** /**
......
...@@ -31,8 +31,17 @@ ...@@ -31,8 +31,17 @@
#ifndef CPPA_ACTOR_BEHAVIOR_HPP #ifndef CPPA_ACTOR_BEHAVIOR_HPP
#define CPPA_ACTOR_BEHAVIOR_HPP #define CPPA_ACTOR_BEHAVIOR_HPP
#include <functional>
#include "cppa/config.hpp" #include "cppa/config.hpp"
#include "cppa/extend.hpp"
#include "cppa/actor_state.hpp"
#include "cppa/local_actor.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 { namespace cppa {
...@@ -45,47 +54,167 @@ enum class resume_result { ...@@ -45,47 +54,167 @@ enum class resume_result {
}; };
enum scheduled_actor_type { enum scheduled_actor_type {
context_switching_impl, context_switching_impl, // enqueued to the job queue on startup
event_based_impl 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. * @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: 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. * @brief Intrusive next pointer needed by the scheduler's job queue.
*/ */
scheduled_actor* next; 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; 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); 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; 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; 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: protected:
scheduler* m_scheduler; scheduler* m_scheduler;
bool m_hidden; bool m_hidden;
mailbox_type m_mailbox;
bool initialized();
}; };
typedef intrusive_ptr<scheduled_actor> scheduled_actor_ptr; typedef intrusive_ptr<scheduled_actor> scheduled_actor_ptr;
inline bool scheduled_actor::is_hidden() const {
return m_hidden;
}
} // namespace cppa } // namespace cppa
#endif // CPPA_ACTOR_BEHAVIOR_HPP #endif // CPPA_ACTOR_BEHAVIOR_HPP
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <type_traits>
#include "cppa/atom.hpp" #include "cppa/atom.hpp"
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
...@@ -42,32 +43,32 @@ ...@@ -42,32 +43,32 @@
#include "cppa/cow_tuple.hpp" #include "cppa/cow_tuple.hpp"
#include "cppa/attachable.hpp" #include "cppa/attachable.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/scheduling_hint.hpp" #include "cppa/spawn_options.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/util/duration.hpp" #include "cppa/util/duration.hpp"
namespace cppa { namespace cppa {
class self_type; class self_type;
class scheduled_actor;
class scheduler_helper; class scheduler_helper;
namespace detail { class singleton_manager; } // namespace detail
typedef std::function<void()> void_function;
typedef std::function<void(local_actor*)> init_callback;
namespace detail { namespace detail {
// forwards self_type as actor_ptr, otherwise equal to std::forward
template<typename T> template<typename T>
struct spawn_fwd_ { struct is_self {
static inline T&& _(T&& arg) { return std::move(arg); } typedef typename util::rm_ref<T>::type plain_type;
static inline T& _(T& arg) { return arg; } static constexpr bool value = std::is_same<plain_type,self_type>::value;
static inline const T& _(const T& arg) { return arg; }
};
template<>
struct spawn_fwd_<self_type> {
static inline actor_ptr _(const self_type& s) { return s.get(); }
}; };
class singleton_manager; template<typename T, typename U>
auto fwd(U& arg, typename std::enable_if<!is_self<T>::value>::type* = 0)
-> decltype(std::forward<T>(arg)) {
return std::forward<T>(arg);
}
template<typename T, typename U>
local_actor* fwd(U& arg, typename std::enable_if<is_self<T>::value>::type* = 0){
return arg;
}
} // namespace detail } // namespace detail
/** /**
...@@ -78,7 +79,7 @@ class scheduler { ...@@ -78,7 +79,7 @@ class scheduler {
scheduler_helper* m_helper; scheduler_helper* m_helper;
channel* delayed_send_helper(); const actor_ptr& delayed_send_helper();
friend class detail::singleton_manager; friend class detail::singleton_manager;
...@@ -107,6 +108,9 @@ class scheduler { ...@@ -107,6 +108,9 @@ class scheduler {
public: public:
typedef std::function<void(local_actor*)> init_callback;
typedef std::function<void()> void_function;
const actor_ptr& printer() const; const actor_ptr& printer() const;
virtual void enqueue(scheduled_actor*) = 0; virtual void enqueue(scheduled_actor*) = 0;
...@@ -157,76 +161,25 @@ class scheduler { ...@@ -157,76 +161,25 @@ class scheduler {
} }
/** /**
* @brief Spawns a new actor that executes <code>fun()</code> * @brief Executes @p ptr in this scheduler.
* with the scheduling policy @p hint if possible.
*/ */
virtual actor_ptr spawn(void_function fun, scheduling_hint hint) = 0; virtual actor_ptr exec(spawn_options opts, scheduled_actor_ptr ptr) = 0;
/** /**
* @brief Spawns a new actor that executes <code>behavior()</code> * @brief Creates a new actor from @p actor_behavior and executes it
* with the scheduling policy @p hint if possible and calls * in this scheduler.
* <code>init_cb</code> after the actor is initialized but before
* it starts execution.
*/ */
virtual actor_ptr spawn(void_function fun, virtual actor_ptr exec(spawn_options opts,
init_callback init_cb, init_callback init_cb,
scheduling_hint hint) = 0; void_function actor_behavior) = 0;
/**
* @brief Spawns a new event-based actor.
*/
virtual actor_ptr spawn(scheduled_actor* what,
scheduling_hint hint = scheduled) = 0;
/** template<typename F, typename T, typename... Ts>
* @brief Spawns a new event-based actor and calls actor_ptr exec(spawn_options opts, init_callback cb,
* <code>init_cb</code> after the actor is initialized but before F f, T&& a0, Ts&&... as) {
* it starts execution. return this->exec(opts, cb, std::bind(f, detail::fwd<T>(a0),
*/ detail::fwd<Ts>(as)...));
virtual actor_ptr spawn(scheduled_actor* what,
init_callback init_cb,
scheduling_hint hint = scheduled) = 0;
// hide implementation details for documentation
# ifndef CPPA_DOCUMENTATION
template<typename Fun, typename Arg0, typename... Args>
actor_ptr spawn_impl(scheduling_hint hint, Fun&& fun, Arg0&& arg0, Args&&... args) {
return this->spawn(
std::bind(
std::forward<Fun>(fun),
detail::spawn_fwd_<typename util::rm_ref<Arg0>::type>::_(arg0),
detail::spawn_fwd_<typename util::rm_ref<Args>::type>::_(args)...),
hint);
} }
template<typename Fun>
actor_ptr spawn_impl(scheduling_hint hint, Fun&& fun) {
return this->spawn(std::forward<Fun>(fun), hint);
}
template<typename InitCallback, typename Fun, typename Arg0, typename... Args>
actor_ptr spawn_cb_impl(scheduling_hint hint,
InitCallback&& init_cb,
Fun&& fun, Arg0&& arg0, Args&&... args) {
return this->spawn(
std::bind(
std::forward<Fun>(fun),
detail::spawn_fwd_<typename util::rm_ref<Arg0>::type>::_(arg0),
detail::spawn_fwd_<typename util::rm_ref<Args>::type>::_(args)...),
std::forward<InitCallback>(init_cb),
hint);
}
template<typename InitCallback, typename Fun>
actor_ptr spawn_cb_impl(scheduling_hint hint, InitCallback&& init_cb, Fun&& fun) {
return this->spawn(std::forward<Fun>(fun),
std::forward<InitCallback>(init_cb),
hint);
}
# endif // CPPA_DOCUMENTATION
}; };
/** /**
...@@ -243,11 +196,6 @@ void set_scheduler(scheduler* sched); ...@@ -243,11 +196,6 @@ void set_scheduler(scheduler* sched);
*/ */
void set_default_scheduler(size_t num_threads); void set_default_scheduler(size_t num_threads);
/**
* @brief Returns the currently running scheduler.
*/
scheduler* get_scheduler();
} // namespace cppa } // namespace cppa
#endif // CPPA_SCHEDULER_HPP #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
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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_SPAWN_OPTIONS_HPP
#define CPPA_SPAWN_OPTIONS_HPP
namespace cppa {
/**
* @ingroup ActorCreation
* @{
*/
/**
* @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,
monitor_flag = 0x02,
detach_flag = 0x04,
hide_flag = 0x08,
blocking_api_flag = 0x10
};
#endif
#ifndef CPPA_DOCUMENTATION
namespace {
#endif
/**
* @brief Denotes default settings.
*/
constexpr spawn_options no_spawn_options = spawn_options::no_flags;
/**
* @brief Causes @p spawn to call <tt>self->monitor(...)</tt> immediately
* after the new actor was spawned.
*/
constexpr spawn_options monitored = spawn_options::monitor_flag;
/**
* @brief Causes @p spawn to call <tt>self->link_to(...)</tt> immediately
* after the new actor was spawned.
*/
constexpr spawn_options linked = spawn_options::link_flag;
/**
* @brief Causes the new actor to opt out of the cooperative scheduling.
*/
constexpr spawn_options detached = spawn_options::detach_flag;
/**
* @brief Causes the runtime to ignore the new actor in
* {@link await_all_others_done()}.
*/
constexpr spawn_options hidden = spawn_options::hide_flag;
/**
* @brief Causes the new actor to opt in to the blocking API of libcppa,
* i.e., the actor uses a context-switching or thread-based backend
* instead of the default event-based implementation.
*/
constexpr spawn_options blocking_api = spawn_options::blocking_api_flag;
#ifndef CPPA_DOCUMENTATION
} // namespace <anonymous>
#endif
/**
* @brief Concatenates two {@link spawn_options}.
* @relates spawn_options
*/
constexpr spawn_options operator+(const spawn_options& lhs,
const spawn_options& rhs) {
return static_cast<spawn_options>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
/**
* @brief Checks wheter @p haystack contains @p needle.
* @relates spawn_options
*/
constexpr bool has_spawn_option(spawn_options haystack, spawn_options needle) {
return (static_cast<int>(haystack) & static_cast<int>(needle)) != 0;
}
/**
* @brief Checks wheter the {@link detached} flag is set in @p opts.
* @relates spawn_options
*/
constexpr bool has_detach_flag(spawn_options opts) {
return has_spawn_option(opts, detached);
}
/**
* @brief Checks wheter the {@link hidden} flag is set in @p opts.
* @relates spawn_options
*/
constexpr bool has_hide_flag(spawn_options opts) {
return has_spawn_option(opts, hidden);
}
/**
* @brief Checks wheter the {@link linked} flag is set in @p opts.
* @relates spawn_options
*/
constexpr bool has_link_flag(spawn_options opts) {
return has_spawn_option(opts, linked);
}
/**
* @brief Checks wheter the {@link monitored} flag is set in @p opts.
* @relates spawn_options
*/
constexpr bool has_monitor_flag(spawn_options opts) {
return has_spawn_option(opts, monitored);
}
/**
* @brief Checks wheter the {@link blocking_api} flag is set in @p opts.
* @relates spawn_options
*/
constexpr bool has_blocking_api_flag(spawn_options opts) {
return has_spawn_option(opts, blocking_api);
}
/** @} */
} // namespace cppa
#endif // CPPA_SPAWN_OPTIONS_HPP
...@@ -31,24 +31,36 @@ ...@@ -31,24 +31,36 @@
#ifndef CPPA_STACKED_ACTOR_MIXIN_HPP #ifndef CPPA_STACKED_ACTOR_MIXIN_HPP
#define CPPA_STACKED_ACTOR_MIXIN_HPP #define CPPA_STACKED_ACTOR_MIXIN_HPP
#include <chrono>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/receive_policy.hpp" #include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/recursive_queue_node.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: public:
static constexpr auto receive_flag = detail::rp_nestable;
virtual void dequeue(partial_function& fun) { virtual void dequeue(partial_function& fun) {
m_recv_policy.receive(dthis(), fun); m_recv_policy.receive(dthis(), fun);
} }
...@@ -62,19 +74,28 @@ class stacked_actor_mixin : public Base { ...@@ -62,19 +74,28 @@ class stacked_actor_mixin : public Base {
} }
virtual void run() { virtual void run() {
if (!this->m_bhvr_stack.empty()) { if (!dthis()->m_bhvr_stack.empty()) {
this->m_bhvr_stack.exec(m_recv_policy, dthis()); dthis()->exec_behavior_stack();
} }
if (m_behavior) { if (m_behavior) {
m_behavior(); m_behavior();
} }
} }
protected: inline void set_behavior(std::function<void()> fun) {
m_behavior = std::move(fun);
}
stacked_actor_mixin() = default; virtual void quit(std::uint32_t reason) {
this->cleanup(reason);
throw actor_exited(reason);
}
stacked_actor_mixin(std::function<void()> f) : m_behavior(std::move(f)) { } protected:
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) { virtual void do_become(behavior&& bhvr, bool discard_old) {
become_impl(std::move(bhvr), discard_old, message_id_t()); become_impl(std::move(bhvr), discard_old, message_id_t());
...@@ -85,36 +106,42 @@ class stacked_actor_mixin : public Base { ...@@ -85,36 +106,42 @@ class stacked_actor_mixin : public Base {
} }
virtual bool has_behavior() { virtual bool has_behavior() {
return static_cast<bool>(m_behavior) return static_cast<bool>(m_behavior) || !dthis()->m_bhvr_stack.empty();
|| !this->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: private:
std::function<void()> m_behavior; std::function<void()> m_behavior;
receive_policy m_recv_policy; detail::receive_policy m_recv_policy;
inline Derived* dthis() {
return static_cast<Derived*>(this);
}
void become_impl(behavior&& bhvr, bool discard_old, message_id_t mid) { void become_impl(behavior&& bhvr, bool discard_old, message_id_t mid) {
if (bhvr.timeout().valid()) { if (bhvr.timeout().valid()) {
dthis()->reset_timeout(); dthis()->reset_timeout();
dthis()->request_timeout(bhvr.timeout()); dthis()->request_timeout(bhvr.timeout());
} }
if (!this->m_bhvr_stack.empty() && discard_old) { if (!dthis()->m_bhvr_stack.empty() && discard_old) {
this->m_bhvr_stack.pop_async_back(); 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() { virtual void exec_behavior_stack() {
this->m_bhvr_stack.exec(m_recv_policy, dthis()); 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 #endif // CPPA_STACKED_ACTOR_MIXIN_HPP
...@@ -44,63 +44,39 @@ ...@@ -44,63 +44,39 @@
#include "cppa/atom.hpp" #include "cppa/atom.hpp"
#include "cppa/either.hpp" #include "cppa/either.hpp"
#include "cppa/extend.hpp"
#include "cppa/stacked.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/exit_reason.hpp" #include "cppa/exit_reason.hpp"
#include "cppa/intrusive_ptr.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/receive_policy.hpp"
#include "cppa/detail/stacked_actor_mixin.hpp"
#include "cppa/detail/recursive_queue_node.hpp" #include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/intrusive/blocking_single_reader_queue.hpp" #include "cppa/intrusive/blocking_single_reader_queue.hpp"
namespace cppa { 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 self_type;
class scheduler_helper;
class thread_mapped_actor; class thread_mapped_actor;
namespace detail { template<>
struct has_blocking_receive<thread_mapped_actor> : std::true_type { };
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;
} // namespace detail /**
* @brief An actor running in its own thread.
class thread_mapped_actor : public detail::thread_mapped_actor_base { * @extends local_actor
*/
class thread_mapped_actor : public extend<local_actor,thread_mapped_actor>::with<stacked,mailbox_based> {
friend class self_type; // needs access to cleanup() 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
friend class detail::behavior_stack; // needs access to receive_node() typedef combined_type super;
friend class detail::receive_policy;
typedef detail::thread_mapped_actor_base super;
public: public:
...@@ -108,59 +84,48 @@ class thread_mapped_actor : public detail::thread_mapped_actor_base { ...@@ -108,59 +84,48 @@ class thread_mapped_actor : public detail::thread_mapped_actor_base {
thread_mapped_actor(std::function<void()> fun); 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 reset_timeout() { }
inline void request_timeout(const util::duration&) { } 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: 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: 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; bool m_initialized;
// required by nestable_receive_policy protected:
static const detail::receive_policy_flag receive_flag = detail::rp_nestable;
inline void push_timeout() { } mailbox_type m_mailbox;
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();
}
}; };
typedef intrusive_ptr<thread_mapped_actor> thread_mapped_actor_ptr; typedef intrusive_ptr<thread_mapped_actor> thread_mapped_actor_ptr;
#endif // CPPA_DOCUMENTATION
} // namespace cppa } // namespace cppa
#endif // CPPA_THREAD_BASED_ACTOR_HPP #endif // CPPA_THREAD_BASED_ACTOR_HPP
...@@ -37,18 +37,16 @@ ...@@ -37,18 +37,16 @@
namespace cppa { namespace cppa {
namespace detail { class behavior_impl; }
template<typename F> template<typename F>
struct timeout_definition { struct timeout_definition {
static constexpr bool may_have_timeout = true;
util::duration timeout; util::duration timeout;
F handler; 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; typedef timeout_definition<std::function<void()> > generic_timeout_definition;
} // namespace cppa } // namespace cppa
......
...@@ -35,12 +35,12 @@ ...@@ -35,12 +35,12 @@
namespace cppa { namespace util { namespace cppa { namespace util {
template<typename... BooleanConstants> template<bool... Values>
struct disjunction; struct disjunction;
template<typename Head, typename... Tail> template<bool Head, bool... Tail>
struct disjunction<Head, Tail...> struct disjunction<Head, Tail...>
: std::integral_constant<bool, Head::value || disjunction<Tail...>::value> { : std::integral_constant<bool, Head || disjunction<Tail...>::value> {
}; };
template<> template<>
......
...@@ -53,7 +53,7 @@ namespace cppa { namespace util { ...@@ -53,7 +53,7 @@ namespace cppa { namespace util {
template<typename What, typename With, typename... IfStmt> template<typename What, typename With, typename... IfStmt>
struct replace_type { 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 typedef typename detail::replace_type<do_replace, What, With>::type
type; type;
}; };
......
...@@ -17,33 +17,25 @@ using namespace std; ...@@ -17,33 +17,25 @@ using namespace std;
using namespace cppa; using namespace cppa;
// either taken by a philosopher or available // either taken by a philosopher or available
struct chopstick : sb_actor<chopstick> { void chopstick() {
become(
behavior& init_state; // a reference to available on(atom("take"), arg_match) >> [=](const actor_ptr& philos) {
behavior available; // tell philosopher it took this chopstick
send(philos, atom("taken"), self);
behavior taken_by(const actor_ptr& philos) { // await 'put' message and reject other 'take' messages
// create a behavior new on-the-fly become(
return ( keep_behavior, // "enables" unbecome()
on<atom("take"), actor_ptr>() >> [=](actor_ptr other) { on(atom("take"), arg_match) >> [=](const actor_ptr& other) {
send(other, atom("busy"), this); send(other, atom("busy"), self);
}, },
on(atom("put"), philos) >> [=]() { on(atom("put"), philos) >> [=] {
become(available); // return to previous behaivor, i.e., await next 'take'
unbecome();
} }
); );
} }
chopstick() : init_state(available) {
available = (
on<atom("take"), actor_ptr>() >> [=](actor_ptr philos) {
send(philos, atom("taken"), this);
become(taken_by(philos));
}
); );
} }
};
/* See: http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/ /* See: http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
* *
...@@ -95,10 +87,7 @@ struct philosopher : sb_actor<philosopher> { ...@@ -95,10 +87,7 @@ struct philosopher : sb_actor<philosopher> {
// wait for second chopstick // wait for second chopstick
behavior waiting_for(const actor_ptr& what) { behavior waiting_for(const actor_ptr& what) {
return ( return (
on(atom("taken"), what) >> [=]() { on(atom("taken"), what) >> [=] {
// create message in memory to avoid interleaved
// messages on the terminal
std::ostringstream oss;
aout << name aout << name
<< " has picked up chopsticks with IDs " << " has picked up chopsticks with IDs "
<< left->id() << left->id()
...@@ -109,7 +98,7 @@ struct philosopher : sb_actor<philosopher> { ...@@ -109,7 +98,7 @@ struct philosopher : sb_actor<philosopher> {
delayed_send(this, seconds(5), atom("think")); delayed_send(this, seconds(5), atom("think"));
become(eating); become(eating);
}, },
on(atom("busy"), what) >> [=]() { on(atom("busy"), what) >> [=] {
send((what == left) ? right : left, atom("put"), this); send((what == left) ? right : left, atom("put"), this);
send(this, atom("eat")); send(this, atom("eat"));
become(thinking); become(thinking);
...@@ -121,7 +110,7 @@ struct philosopher : sb_actor<philosopher> { ...@@ -121,7 +110,7 @@ struct philosopher : sb_actor<philosopher> {
: name(n), left(l), right(r) { : name(n), left(l), right(r) {
// a philosopher that receives {eat} stops thinking and becomes hungry // a philosopher that receives {eat} stops thinking and becomes hungry
thinking = ( thinking = (
on(atom("eat")) >> [=]() { on(atom("eat")) >> [=] {
become(hungry); become(hungry);
send(left, atom("take"), this); send(left, atom("take"), this);
send(right, atom("take"), this); send(right, atom("take"), this);
...@@ -129,43 +118,43 @@ struct philosopher : sb_actor<philosopher> { ...@@ -129,43 +118,43 @@ struct philosopher : sb_actor<philosopher> {
); );
// wait for the first answer of a chopstick // wait for the first answer of a chopstick
hungry = ( hungry = (
on(atom("taken"), left) >> [=]() { on(atom("taken"), left) >> [=] {
become(waiting_for(right)); become(waiting_for(right));
}, },
on(atom("taken"), right) >> [=]() { on(atom("taken"), right) >> [=] {
become(waiting_for(left)); become(waiting_for(left));
}, },
on<atom("busy"), actor_ptr>() >> [=]() { on<atom("busy"), actor_ptr>() >> [=] {
become(denied); become(denied);
} }
); );
// philosopher was not able to obtain the first chopstick // philosopher was not able to obtain the first chopstick
denied = ( denied = (
on<atom("taken"), actor_ptr>() >> [=](actor_ptr& ptr) { on(atom("taken"), arg_match) >> [=](const actor_ptr& ptr) {
send(ptr, atom("put"), this); send(ptr, atom("put"), this);
send(this, atom("eat")); send(this, atom("eat"));
become(thinking); become(thinking);
}, },
on<atom("busy"), actor_ptr>() >> [=]() { on<atom("busy"), actor_ptr>() >> [=] {
send(this, atom("eat")); send(this, atom("eat"));
become(thinking); become(thinking);
} }
); );
// philosopher obtained both chopstick and eats (for five seconds) // philosopher obtained both chopstick and eats (for five seconds)
eating = ( eating = (
on(atom("think")) >> [=]() { on(atom("think")) >> [=] {
send(left, atom("put"), this); send(left, atom("put"), this);
send(right, atom("put"), this); send(right, atom("put"), this);
delayed_send(this, seconds(5), atom("eat")); delayed_send(this, seconds(5), atom("eat"));
aout << ( name aout << name
+ " puts down his chopsticks and starts to think\n"); << " puts down his chopsticks and starts to think\n";
become(thinking); become(thinking);
} }
); );
// philosophers start to think after receiving {think} // philosophers start to think after receiving {think}
init_state = ( init_state = (
on(atom("think")) >> [=]() { on(atom("think")) >> [=] {
aout << (name + " starts to think\n"); aout << name << " starts to think\n";
delayed_send(this, seconds(5), atom("eat")); delayed_send(this, seconds(5), atom("eat"));
become(thinking); become(thinking);
} }
...@@ -176,10 +165,10 @@ struct philosopher : sb_actor<philosopher> { ...@@ -176,10 +165,10 @@ struct philosopher : sb_actor<philosopher> {
int main(int, char**) { int main(int, char**) {
// create five chopsticks // create five chopsticks
aout << "chopstick ids:"; aout << "chopstick ids are:";
std::vector<actor_ptr> chopsticks; std::vector<actor_ptr> chopsticks;
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(spawn<chopstick>()); chopsticks.push_back(spawn(chopstick));
aout << " " << chopsticks.back()->id(); aout << " " << chopsticks.back()->id();
} }
aout << endl; aout << endl;
...@@ -194,7 +183,7 @@ int main(int, char**) { ...@@ -194,7 +183,7 @@ int main(int, char**) {
chopsticks[i], chopsticks[i],
chopsticks[(i+1) % chopsticks.size()]); chopsticks[(i+1) % chopsticks.size()]);
} }
// tell philosophers to start thinking // tell all philosophers to start thinking
send(dinner_club, atom("think")); send(dinner_club, atom("think"));
// real philosophers are never done // real philosophers are never done
await_all_others_done(); await_all_others_done();
......
...@@ -4,35 +4,43 @@ ...@@ -4,35 +4,43 @@
using namespace cppa; using namespace cppa;
void echo_actor() { void mirror() {
// wait for a message // wait for messages
receive ( become (
// invoke this lambda expression if we receive a string // invoke this lambda expression if we receive a string
on<std::string>() >> [](const std::string& what) { on_arg_match >> [](const std::string& what) {
// prints "Hello World!" // prints "Hello World!"
std::cout << what << std::endl; std::cout << what << std::endl;
// replies "!dlroW olleH" // replies "!dlroW olleH"
reply(std::string(what.rbegin(), what.rend())); reply(std::string(what.rbegin(), what.rend()));
// terminates this actor
self->quit();
} }
); );
} }
int main() { void hello_world(const actor_ptr& buddy) {
// create a new actor that invokes the function echo_actor // send "Hello World!" to the mirror
auto hello_actor = spawn(echo_actor); send(buddy, "Hello World!");
// send "Hello World!" to our new actor // wait for messages
// note: libcppa converts string literals to std::string become (
send(hello_actor, "Hello World!"); on_arg_match >> [](const std::string& what) {
// wait for a response and print it
receive (
on<std::string>() >> [](const std::string& what) {
// prints "!dlroW olleH" // prints "!dlroW olleH"
std::cout << what << std::endl; std::cout << what << std::endl;
// terminate this actor
self->quit();
} }
); );
// wait until all other actors we've spawned are done }
int main() {
// create a new actor that calls 'mirror()'
auto mirror_actor = spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)'
spawn(hello_world, mirror_actor);
// wait until all other actors we have spawned are done
await_all_others_done(); await_all_others_done();
// done // run cleanup code before exiting main
shutdown(); shutdown();
return 0; return 0;
} }
...@@ -13,7 +13,7 @@ using std::endl; ...@@ -13,7 +13,7 @@ using std::endl;
using namespace cppa; using namespace cppa;
// implementation using the blocking API // implementation using the blocking API
void math_fun() { void blocking_math_fun() {
bool done = false; bool done = false;
do_receive ( do_receive (
// "arg_match" matches the parameter types of given lambda expression // "arg_match" matches the parameter types of given lambda expression
...@@ -23,22 +23,19 @@ void math_fun() { ...@@ -23,22 +23,19 @@ void math_fun() {
on(atom("plus"), arg_match) >> [](int a, int b) { on(atom("plus"), arg_match) >> [](int a, int b) {
reply(atom("result"), a + b); reply(atom("result"), a + b);
}, },
on<atom("minus"), int, int>() >> [](int a, int b) { on(atom("minus"), arg_match) >> [](int a, int b) {
reply(atom("result"), a - b); reply(atom("result"), a - b);
}, },
on(atom("quit")) >> [&]() { on(atom("quit")) >> [&]() {
// note: quit(exit_reason::normal) would terminate the actor // note: this actor uses the blocking API, hence self->quit()
// but is best avoided since it forces stack unwinding // would force stack unwinding by throwing an exception
// by throwing an exception
done = true; done = true;
} }
) ).until(gref(done));
.until(gref(done));
} }
// implementation using the event-based API // implementation using the event-based API
struct math_actor : event_based_actor { void math_fun() {
void init() {
// execute this behavior until actor terminates // execute this behavior until actor terminates
become ( become (
on(atom("plus"), arg_match) >> [](int a, int b) { on(atom("plus"), arg_match) >> [](int a, int b) {
...@@ -49,14 +46,12 @@ struct math_actor : event_based_actor { ...@@ -49,14 +46,12 @@ struct math_actor : event_based_actor {
}, },
// the [=] capture copies the 'this' pointer into the lambda // the [=] capture copies the 'this' pointer into the lambda
// thus, it has access to all members and member functions // thus, it has access to all members and member functions
on(atom("quit")) >> [=]() { on(atom("quit")) >> [=] {
// set an empty behavior // terminate actor with normal exit reason
// (terminates actor with normal exit reason) self->quit();
quit();
} }
); );
} }
};
// utility function // utility function
int fetch_result(actor_ptr& calculator, atom_value operation, int a, int b) { int fetch_result(actor_ptr& calculator, atom_value operation, int a, int b) {
...@@ -72,9 +67,9 @@ int fetch_result(actor_ptr& calculator, atom_value operation, int a, int b) { ...@@ -72,9 +67,9 @@ int fetch_result(actor_ptr& calculator, atom_value operation, int a, int b) {
int main() { int main() {
// spawn a context-switching actor that invokes math_fun // spawn a context-switching actor that invokes math_fun
auto a1 = spawn(math_fun); auto a1 = spawn<blocking_api>(blocking_math_fun);
// spawn an event-based math actor // spawn an event-based math actor
auto a2 = spawn<math_actor>(); auto a2 = spawn(math_fun);
// do some testing on both implementations // do some testing on both implementations
assert((fetch_result(a1, atom("plus"), 1, 2) == 3)); assert((fetch_result(a1, atom("plus"), 1, 2) == 3));
assert((fetch_result(a2, atom("plus"), 1, 2) == 3)); assert((fetch_result(a2, atom("plus"), 1, 2) == 3));
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 @@ ...@@ -32,6 +32,8 @@
namespace cppa { namespace detail { 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 { bool abstract_tuple::equals(const abstract_tuple &other) const {
return this == &other return this == &other
|| ( size() == other.size() || ( size() == other.size()
...@@ -39,9 +41,7 @@ bool abstract_tuple::equals(const abstract_tuple &other) const { ...@@ -39,9 +41,7 @@ bool abstract_tuple::equals(const abstract_tuple &other) const {
} }
abstract_tuple::abstract_tuple(const abstract_tuple& other) abstract_tuple::abstract_tuple(const abstract_tuple& other)
: ref_counted() : m_is_dynamic(other.m_is_dynamic) { }
, m_impl_type(other.m_impl_type) {
}
const std::type_info* abstract_tuple::type_token() const { const std::type_info* abstract_tuple::type_token() const {
return &typeid(void); return &typeid(void);
......
...@@ -36,7 +36,10 @@ ...@@ -36,7 +36,10 @@
#include <stdexcept> #include <stdexcept>
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
#include "cppa/config.hpp"
#include "cppa/logging.hpp"
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/singletons.hpp"
#include "cppa/util/shared_spinlock.hpp" #include "cppa/util/shared_spinlock.hpp"
#include "cppa/util/shared_lock_guard.hpp" #include "cppa/util/shared_lock_guard.hpp"
...@@ -46,26 +49,161 @@ ...@@ -46,26 +49,161 @@
namespace cppa { namespace cppa {
namespace { namespace { typedef std::unique_lock<std::mutex> guard_type; }
inline detail::actor_registry& registry() {
return *(detail::singleton_manager::get_actor_registry());
}
} // 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)); enqueue(sender, std::move(msg));
return false; 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)); sync_enqueue(ptr, id, std::move(msg));
return false; 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 } // namespace cppa
...@@ -76,7 +76,7 @@ void actor_registry::put(actor_id key, const actor_ptr& value) { ...@@ -76,7 +76,7 @@ void actor_registry::put(actor_id key, const actor_ptr& value) {
} }
} }
if (add_attachable) { if (add_attachable) {
CPPA_LOG_INFO("added " << key); CPPA_LOG_INFO("added actor with ID " << key);
struct eraser : attachable { struct eraser : attachable {
actor_id m_id; actor_id m_id;
actor_registry* m_registry; actor_registry* m_registry;
...@@ -88,7 +88,7 @@ void actor_registry::put(actor_id key, const actor_ptr& value) { ...@@ -88,7 +88,7 @@ void actor_registry::put(actor_id key, const actor_ptr& value) {
return false; 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) { ...@@ -97,7 +97,8 @@ void actor_registry::erase(actor_id key, std::uint32_t reason) {
auto i = m_entries.find(key); auto i = m_entries.find(key);
if (i != m_entries.end()) { if (i != m_entries.end()) {
auto& entry = i->second; 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.first = nullptr;
entry.second = reason; entry.second = reason;
} }
......
...@@ -29,26 +29,20 @@ ...@@ -29,26 +29,20 @@
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/singletons.hpp"
#include "cppa/detail/empty_tuple.hpp" #include "cppa/detail/empty_tuple.hpp"
#include "cppa/detail/singleton_manager.hpp"
namespace cppa { namespace cppa {
namespace { any_tuple::any_tuple() : m_vals(get_empty_tuple()) { }
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(detail::abstract_tuple* ptr) : m_vals(ptr) { } 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); 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) { any_tuple& any_tuple::operator=(any_tuple&& other) {
m_vals.swap(other.m_vals); m_vals.swap(other.m_vals);
...@@ -56,7 +50,7 @@ any_tuple& any_tuple::operator=(any_tuple&& other) { ...@@ -56,7 +50,7 @@ any_tuple& any_tuple::operator=(any_tuple&& other) {
} }
void any_tuple::reset() { void any_tuple::reset() {
m_vals.reset(s_empty_tuple()); m_vals.reset(get_empty_tuple());
} }
size_t any_tuple::size() const { size_t any_tuple::size() const {
......
...@@ -32,7 +32,9 @@ ...@@ -32,7 +32,9 @@
namespace cppa { namespace cppa {
attachable::~attachable() { attachable::~attachable() { }
}
attachable::token::token(const std::type_info& tinfo, const void* vptr)
: subtype(tinfo), ptr(vptr) { }
} // namespace cppa::detail } // namespace cppa::detail
...@@ -38,12 +38,37 @@ ...@@ -38,12 +38,37 @@
namespace cppa { namespace cppa {
context_switching_actor::context_switching_actor() context_switching_actor::context_switching_actor(std::function<void()> fun)
: m_fiber(&context_switching_actor::trampoline, this) { : 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) detail::recursive_queue_node* context_switching_actor::await_message(const timeout_type&) {
: super(std::move(fun)), m_fiber(&context_switching_actor::trampoline, this) { // 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) { void context_switching_actor::trampoline(void* this_ptr) {
...@@ -64,31 +89,12 @@ 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::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() { scheduled_actor_type context_switching_actor::impl_type() {
return context_switching_impl; return context_switching_impl;
} }
resume_result context_switching_actor::resume(util::fiber* from, actor_ptr& next_job) { 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(from != nullptr);
CPPA_REQUIRE(next_job == nullptr); CPPA_REQUIRE(next_job == nullptr);
using namespace detail; using namespace detail;
...@@ -107,15 +113,15 @@ resume_result context_switching_actor::resume(util::fiber* from, actor_ptr& next ...@@ -107,15 +113,15 @@ resume_result context_switching_actor::resume(util::fiber* from, actor_ptr& next
CPPA_REQUIRE(next_job == nullptr); CPPA_REQUIRE(next_job == nullptr);
m_chained_actor.swap(next_job); m_chained_actor.swap(next_job);
CPPA_REQUIRE(m_chained_actor == nullptr); CPPA_REQUIRE(m_chained_actor == nullptr);
switch (compare_exchange_state(abstract_scheduled_actor::about_to_block, switch (compare_exchange_state(actor_state::about_to_block,
abstract_scheduled_actor::blocked)) { actor_state::blocked)) {
case abstract_scheduled_actor::ready: { case actor_state::ready: {
// restore variables // restore variables
m_chained_actor.swap(next_job); m_chained_actor.swap(next_job);
CPPA_REQUIRE(next_job == nullptr); CPPA_REQUIRE(next_job == nullptr);
break; break;
} }
case abstract_scheduled_actor::blocked: { case actor_state::blocked: {
// wait until someone re-schedules that actor // wait until someone re-schedules that actor
return resume_result::actor_blocked; return resume_result::actor_blocked;
} }
......
...@@ -63,7 +63,7 @@ void default_actor_addressing::write(serializer* sink, const actor_ptr& ptr) { ...@@ -63,7 +63,7 @@ void default_actor_addressing::write(serializer* sink, const actor_ptr& ptr) {
else { else {
// local actor? // local actor?
if (!ptr->is_proxy()) { 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; auto pinf = m_pinf;
if (ptr->is_proxy()) { if (ptr->is_proxy()) {
...@@ -101,7 +101,7 @@ actor_ptr default_actor_addressing::read(deserializer* source) { ...@@ -101,7 +101,7 @@ actor_ptr default_actor_addressing::read(deserializer* source) {
// local actor? // local actor?
auto pinf = process_information::get(); auto pinf = process_information::get();
if (pid == pinf->process_id() && nid == pinf->node_id()) { 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 { else {
process_information tmp(pid, nid); process_information tmp(pid, nid);
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "cppa/detail/memory.hpp" #include "cppa/detail/memory.hpp"
#include "cppa/detail/singleton_manager.hpp" #include "cppa/detail/singleton_manager.hpp"
#include "cppa/detail/sync_request_bouncer.hpp"
using namespace std; using namespace std;
...@@ -54,13 +55,13 @@ default_actor_proxy::default_actor_proxy(actor_id mid, ...@@ -54,13 +55,13 @@ default_actor_proxy::default_actor_proxy(actor_id mid,
: super(mid), m_proto(parent), m_pinf(pinfo) { } : super(mid), m_proto(parent), m_pinf(pinfo) { }
default_actor_proxy::~default_actor_proxy() { default_actor_proxy::~default_actor_proxy() {
CPPA_LOG_TRACE("node = " << to_string(*m_pinf) << ", aid = " << id());
auto aid = id(); auto aid = id();
auto node = m_pinf; auto node = m_pinf;
auto proto = m_proto; auto proto = m_proto;
proto->run_later([aid, node, proto] { proto->run_later([aid, node, proto] {
CPPA_LOGF_TRACE("lambda from ~default_actor_proxy" CPPA_LOGC_TRACE("cppa::network::default_actor_proxy",
<< "; node = " << to_string(*node) << ", aid " << aid "~default_actor_proxy$run_later",
"node = " << to_string(*node) << ", aid " << aid
<< ", proto = " << to_string(proto->identifier())); << ", proto = " << to_string(proto->identifier()));
proto->addressing()->erase(*node, aid); proto->addressing()->erase(*node, aid);
auto p = proto->get_peer(*node); auto p = proto->get_peer(*node);
...@@ -94,10 +95,12 @@ void default_actor_proxy::forward_msg(const actor_ptr& sender, ...@@ -94,10 +95,12 @@ void default_actor_proxy::forward_msg(const actor_ptr& sender,
switch (m_pending_requests.enqueue(new_req_info(sender, mid))) { switch (m_pending_requests.enqueue(new_req_info(sender, mid))) {
case intrusive::queue_closed: { case intrusive::queue_closed: {
if (sender) { if (sender) {
intrusive_ptr<default_actor_proxy> _this{this};
auto rsn = exit_reason(); auto rsn = exit_reason();
m_proto->run_later([_this,rsn,sender,mid] { m_proto->run_later([rsn,sender,mid] {
detail::sync_request_bouncer f{_this.get(), rsn}; 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); f(sender.get(), mid);
}); });
} }
...@@ -110,13 +113,16 @@ void default_actor_proxy::forward_msg(const actor_ptr& sender, ...@@ -110,13 +113,16 @@ void default_actor_proxy::forward_msg(const actor_ptr& sender,
auto node = m_pinf; auto node = m_pinf;
auto proto = m_proto; auto proto = m_proto;
m_proto->run_later([hdr, msg, node, 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); proto->enqueue(*node, hdr, msg);
}); });
} }
void default_actor_proxy::enqueue(actor* sender, any_tuple msg) { void default_actor_proxy::enqueue(const actor_ptr& sender, any_tuple msg) {
CPPA_LOG_TRACE(CPPA_ARG(sender) << ", " << CPPA_TARG(msg, to_string)); CPPA_LOG_TRACE(CPPA_TARG(sender, to_string) << ", "
<< CPPA_TARG(msg, to_string));
auto& arr = detail::static_types_array<atom_value, uint32_t>::arr; auto& arr = detail::static_types_array<atom_value, uint32_t>::arr;
if ( msg.size() == 2 if ( msg.size() == 2
&& msg.type_at(0) == arr[0] && msg.type_at(0) == arr[0]
...@@ -126,10 +132,11 @@ void default_actor_proxy::enqueue(actor* sender, any_tuple msg) { ...@@ -126,10 +132,11 @@ void default_actor_proxy::enqueue(actor* sender, any_tuple msg) {
intrusive_ptr<default_actor_proxy> _this{this}; intrusive_ptr<default_actor_proxy> _this{this};
auto reason = msg.get_as<uint32_t>(1); auto reason = msg.get_as<uint32_t>(1);
m_proto->run_later([_this, reason] { 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); _this->cleanup(reason);
// make sure cleanup is done before closing requests queue detail::sync_request_bouncer f{reason};
std::atomic_thread_fence(std::memory_order_seq_cst);
detail::sync_request_bouncer f{_this.get(), reason};
_this->m_pending_requests.close([&](const sync_request_info& e) { _this->m_pending_requests.close([&](const sync_request_info& e) {
f(e.sender.get(), e.mid); f(e.sender.get(), e.mid);
}); });
...@@ -139,9 +146,12 @@ void default_actor_proxy::enqueue(actor* sender, any_tuple msg) { ...@@ -139,9 +146,12 @@ void default_actor_proxy::enqueue(actor* sender, any_tuple msg) {
forward_msg(sender, move(msg)); forward_msg(sender, move(msg));
} }
void default_actor_proxy::sync_enqueue(actor* sender, message_id_t mid, any_tuple msg) { void default_actor_proxy::sync_enqueue(const actor_ptr& sender,
CPPA_LOG_TRACE(CPPA_ARG(sender) << ", " << CPPA_MARG(mid, integer_value) message_id_t mid,
<< ", " << CPPA_TARG(msg, to_string)); 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); forward_msg(sender, move(msg), mid);
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "cppa/match.hpp" #include "cppa/match.hpp"
#include "cppa/logging.hpp" #include "cppa/logging.hpp"
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/singletons.hpp"
#include "cppa/exit_reason.hpp" #include "cppa/exit_reason.hpp"
#include "cppa/actor_proxy.hpp" #include "cppa/actor_proxy.hpp"
#include "cppa/binary_serializer.hpp" #include "cppa/binary_serializer.hpp"
...@@ -195,7 +196,7 @@ void default_peer::monitor(const actor_ptr&, ...@@ -195,7 +196,7 @@ void default_peer::monitor(const actor_ptr&,
CPPA_LOG_ERROR("received MONITOR from invalid peer"); CPPA_LOG_ERROR("received MONITOR from invalid peer");
return; 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(); auto pself = process_information::get();
if (*node == *pself) { if (*node == *pself) {
...@@ -221,7 +222,9 @@ void default_peer::monitor(const actor_ptr&, ...@@ -221,7 +222,9 @@ void default_peer::monitor(const actor_ptr&,
default_protocol_ptr proto = m_parent; default_protocol_ptr proto = m_parent;
entry.first->attach_functor([=](uint32_t reason) { entry.first->attach_functor([=](uint32_t reason) {
proto->run_later([=] { 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); auto p = proto->get_peer(*node);
if (p) p->enqueue(make_any_tuple(atom("KILL_PROXY"), pself, aid, reason)); if (p) p->enqueue(make_any_tuple(atom("KILL_PROXY"), pself, aid, reason));
}); });
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "cppa/logging.hpp" #include "cppa/logging.hpp"
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/singletons.hpp"
#include "cppa/network/middleman.hpp" #include "cppa/network/middleman.hpp"
#include "cppa/network/default_peer.hpp" #include "cppa/network/default_peer.hpp"
...@@ -88,11 +89,12 @@ void default_protocol::publish(const actor_ptr& whom, ...@@ -88,11 +89,12 @@ void default_protocol::publish(const actor_ptr& whom,
if (!whom) return; if (!whom) return;
CPPA_REQUIRE(args.size() == 0); CPPA_REQUIRE(args.size() == 0);
static_cast<void>(args); // keep compiler happy 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; default_protocol_ptr proto = this;
auto impl = make_counted<default_peer_acceptor>(this, move(ptr), whom); auto impl = make_counted<default_peer_acceptor>(this, move(ptr), whom);
run_later([=] { 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->m_acceptors[whom].push_back(impl);
proto->continue_reader(impl.get()); proto->continue_reader(impl.get());
}); });
...@@ -102,11 +104,10 @@ void default_protocol::unpublish(const actor_ptr& whom) { ...@@ -102,11 +104,10 @@ void default_protocol::unpublish(const actor_ptr& whom) {
CPPA_LOG_TRACE("whom = " << to_string(whom)); CPPA_LOG_TRACE("whom = " << to_string(whom));
default_protocol_ptr proto = this; default_protocol_ptr proto = this;
run_later([=] { 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]; auto& acceptors = m_acceptors[whom];
for (auto& ptr : acceptors) { for (auto& ptr : acceptors) proto->stop_reader(ptr.get());
proto->stop_reader(ptr.get());
}
m_acceptors.erase(whom); m_acceptors.erase(whom);
}); });
} }
...@@ -123,8 +124,12 @@ void default_protocol::register_peer(const process_information& node, ...@@ -123,8 +124,12 @@ void default_protocol::register_peer(const process_information& node,
auto tmp = entry.queue->pop(); auto tmp = entry.queue->pop();
ptr->enqueue(tmp.first, tmp.second); 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) { 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, ...@@ -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::cerr << "*** warning: remote_actor() called to access a local actor\n"
<< std::flush; << std::flush;
# endif # endif
return singleton_manager::get_actor_registry()->get(remote_aid); return get_actor_registry()->get(remote_aid);
} }
default_protocol_ptr proto = this; default_protocol_ptr proto = this;
intrusive::blocking_single_reader_queue<remote_actor_result> q; intrusive::blocking_single_reader_queue<remote_actor_result> q;
run_later([proto, io, pinfptr, remote_aid, &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); auto pp = proto->get_peer(*pinfptr);
CPPA_LOGF_INFO_IF(pp, "connection already exists (re-use old one)"); CPPA_LOGF_INFO_IF(pp, "connection already exists (re-use old one)");
if (!pp) proto->new_peer(io.first, io.second, pinfptr); if (!pp) proto->new_peer(io.first, io.second, pinfptr);
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
namespace cppa { namespace detail { 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 { size_t empty_tuple::size() const {
return 0; return 0;
......
...@@ -31,12 +31,48 @@ ...@@ -31,12 +31,48 @@
#include <iostream> #include <iostream>
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/cppa.hpp"
#include "cppa/self.hpp" #include "cppa/self.hpp"
#include "cppa/logging.hpp"
#include "cppa/event_based_actor.hpp" #include "cppa/event_based_actor.hpp"
namespace cppa { namespace cppa {
event_based_actor::event_based_actor() : super(super::blocked) { } class default_scheduled_actor : public event_based_actor {
typedef event_based_actor super;
public:
typedef std::function<void()> fun_type;
default_scheduled_actor(fun_type&& fun) : m_fun(std::move(fun)) { }
void init() {
become (
on(atom("RUN")) >> [=] {
CPPA_LOGS_TRACE("init$lambda", "");
unbecome();
m_fun();
}
);
}
scheduled_actor_type impl_type() {
return default_event_based_impl;
}
private:
fun_type m_fun;
};
intrusive_ptr<event_based_actor> event_based_actor::from(std::function<void()> fun) {
return detail::memory::create<default_scheduled_actor>(std::move(fun));
}
event_based_actor::event_based_actor() : super(actor_state::blocked, true) { }
void event_based_actor::dequeue(behavior&) { void event_based_actor::dequeue(behavior&) {
quit(exit_reason::unallowed_function_call); quit(exit_reason::unallowed_function_call);
...@@ -51,24 +87,14 @@ void event_based_actor::dequeue_response(behavior&, message_id_t) { ...@@ -51,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) { resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) {
# ifdef CPPA_DEBUG CPPA_LOG_TRACE("id = " << id() << ", state = " << static_cast<int>(state()));
auto st = m_state.load(); CPPA_REQUIRE( state() == actor_state::ready
switch (st) { || state() == actor_state::pending);
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
scoped_self_setter sss{this}; scoped_self_setter sss{this};
auto done_cb = [&]() { 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.clear();
m_bhvr_stack.cleanup(); m_bhvr_stack.cleanup();
on_exit(); on_exit();
...@@ -77,45 +103,52 @@ resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) { ...@@ -77,45 +103,52 @@ resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) {
}; };
CPPA_REQUIRE(next_job == nullptr); CPPA_REQUIRE(next_job == nullptr);
try { try {
detail::recursive_queue_node* e = nullptr; //auto e = m_mailbox.try_pop();
for (;;) { for (auto e = m_mailbox.try_pop(); ; e = m_mailbox.try_pop()) {
e = m_mailbox.try_pop(); //e = m_mailbox.try_pop();
if (e == nullptr) { if (e == nullptr) {
CPPA_REQUIRE(next_job == nullptr); CPPA_REQUIRE(next_job == nullptr);
CPPA_LOG_DEBUG("no more element in mailbox; going to block");
next_job.swap(m_chained_actor); 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); std::atomic_thread_fence(std::memory_order_seq_cst);
if (m_mailbox.can_fetch_more() == false) { if (this->m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state( switch (compare_exchange_state(actor_state::about_to_block,
abstract_scheduled_actor::about_to_block, actor_state::blocked)) {
abstract_scheduled_actor::blocked)) { case actor_state::ready:
case abstract_scheduled_actor::ready:
// interrupted by arriving message // interrupted by arriving message
// restore members // restore members
CPPA_REQUIRE(m_chained_actor == nullptr); CPPA_REQUIRE(m_chained_actor == nullptr);
next_job.swap(m_chained_actor); next_job.swap(m_chained_actor);
CPPA_LOG_DEBUG("switched back to ready: "
"interrupted by arriving message");
break; break;
case abstract_scheduled_actor::blocked: case actor_state::blocked:
CPPA_LOG_DEBUG("set state successfully to blocked");
// done setting actor to blocked // done setting actor to blocked
return resume_result::actor_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: default:
CPPA_LOG_ERROR("invalid state");
CPPA_CRITICAL("invalid state"); CPPA_CRITICAL("invalid state");
}; };
} }
else { 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); CPPA_REQUIRE(m_chained_actor == nullptr);
next_job.swap(m_chained_actor); next_job.swap(m_chained_actor);
} }
} }
else if (m_bhvr_stack.invoke(m_policy, this, e)) { 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()) { if (m_bhvr_stack.empty()) {
CPPA_LOG_DEBUG("behavior stack empty");
done_cb(); done_cb();
return resume_result::actor_done; return resume_result::actor_done;
} }
...@@ -123,8 +156,28 @@ resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) { ...@@ -123,8 +156,28 @@ resume_result event_based_actor::resume(util::fiber*, actor_ptr& next_job) {
} }
} }
} }
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(); done_cb();
return resume_result::actor_done; return resume_result::actor_done;
} }
...@@ -149,12 +202,19 @@ void event_based_actor::become_waiting_for(behavior&& bhvr, message_id_t mf) { ...@@ -149,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) { void event_based_actor::quit(std::uint32_t reason) {
if (reason == exit_reason::normal) { CPPA_LOG_TRACE("reason = " << reason
cleanup(exit_reason::normal); << ", class " << detail::demangle(typeid(*this)));
cleanup(reason);
m_bhvr_stack.clear(); m_bhvr_stack.clear();
} if (reason == exit_reason::unallowed_function_call) {
else { CPPA_LOG_WARNING("actor tried to use a blocking function");
abstract_scheduled_actor::quit(reason); // 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 @@ ...@@ -31,6 +31,7 @@
#include "cppa/cppa.hpp" #include "cppa/cppa.hpp"
#include "cppa/group.hpp" #include "cppa/group.hpp"
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/singletons.hpp"
#include "cppa/util/shared_spinlock.hpp" #include "cppa/util/shared_spinlock.hpp"
#include "cppa/util/shared_lock_guard.hpp" #include "cppa/util/shared_lock_guard.hpp"
#include "cppa/util/upgrade_lock_guard.hpp" #include "cppa/util/upgrade_lock_guard.hpp"
...@@ -40,29 +41,21 @@ ...@@ -40,29 +41,21 @@
namespace cppa { 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, intrusive_ptr<group> group::get(const std::string& arg0,
const std::string& arg1) { const std::string& arg1) {
return manager()->get(arg0, arg1); return get_group_manager()->get(arg0, arg1);
} }
intrusive_ptr<group> group::anonymous() { intrusive_ptr<group> group::anonymous() {
return manager()->anonymous(); return get_group_manager()->anonymous();
} }
void group::add_module(group::unique_module_ptr ptr) { 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) { 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, group::subscription::subscription(const channel_ptr& s,
...@@ -108,7 +101,7 @@ struct group_nameserver : event_based_actor { ...@@ -108,7 +101,7 @@ struct group_nameserver : event_based_actor {
}; };
void publish_local_groups_at(std::uint16_t port, const char* addr) { void publish_local_groups_at(std::uint16_t port, const char* addr) {
auto gn = spawn_hidden<group_nameserver>(); auto gn = spawn<group_nameserver,hidden>();
try { try {
publish(gn, port, addr); publish(gn, port, addr);
} }
......
...@@ -66,14 +66,14 @@ class local_group : public group { ...@@ -66,14 +66,14 @@ class local_group : public group {
public: 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); shared_guard guard(m_mtx);
for (auto& s : m_subscribers) { for (auto& s : m_subscribers) {
s->enqueue(sender, msg); 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); send_all_subscribers(sender, msg);
m_broker->enqueue(sender, move(msg)); m_broker->enqueue(sender, move(msg));
} }
...@@ -189,7 +189,7 @@ class local_group_proxy : public local_group { ...@@ -189,7 +189,7 @@ class local_group_proxy : public local_group {
CPPA_REQUIRE(remote_broker != nullptr); CPPA_REQUIRE(remote_broker != nullptr);
CPPA_REQUIRE(remote_broker->is_proxy()); CPPA_REQUIRE(remote_broker->is_proxy());
m_broker = move(remote_broker); m_broker = move(remote_broker);
m_proxy_broker = spawn_hidden<proxy_broker>(this); m_proxy_broker = spawn<proxy_broker,hidden>(this);
} }
group::subscription subscribe(const channel_ptr& who) { group::subscription subscribe(const channel_ptr& who) {
...@@ -215,7 +215,7 @@ class local_group_proxy : public local_group { ...@@ -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 // forward message to the broker
m_broker->enqueue(sender, m_broker->enqueue(sender,
make_any_tuple(atom("FORWARD"), move(msg))); make_any_tuple(atom("FORWARD"), move(msg)));
...@@ -342,7 +342,7 @@ class remote_group : public group { ...@@ -342,7 +342,7 @@ class remote_group : public group {
void unsubscribe(const channel_ptr&) { /* never called */ } 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); m_decorated->enqueue(sender, msg);
} }
...@@ -426,8 +426,11 @@ class remote_group_module : public group::module { ...@@ -426,8 +426,11 @@ class remote_group_module : public group::module {
auto sm = make_counted<shared_map>(); auto sm = make_counted<shared_map>();
group::module_ptr _this = this; group::module_ptr _this = this;
m_map = sm; m_map = sm;
auto worker = spawn<detached_and_hidden>([_this, sm] { auto worker = spawn<blocking_api+hidden>([_this, sm] {
typedef map<string, pair<actor_ptr, vector<pair<string, remote_group_ptr>>>> 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;
peer_map peers; peer_map peers;
receive_loop ( receive_loop (
...@@ -529,7 +532,7 @@ local_group::local_group(bool spawn_local_broker, ...@@ -529,7 +532,7 @@ local_group::local_group(bool spawn_local_broker,
local_group_module* mod, local_group_module* mod,
string id) string id)
: group(mod, move(id)) { : group(mod, move(id)) {
if (spawn_local_broker) m_broker = spawn_hidden<local_broker>(this); if (spawn_local_broker) m_broker = spawn<local_broker, hidden>(this);
} }
void local_group::serialize(serializer* sink) { void local_group::serialize(serializer* sink) {
......
...@@ -30,8 +30,10 @@ ...@@ -30,8 +30,10 @@
#include "cppa/cppa.hpp" #include "cppa/cppa.hpp"
#include "cppa/atom.hpp" #include "cppa/atom.hpp"
#include "cppa/logging.hpp"
#include "cppa/scheduler.hpp" #include "cppa/scheduler.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/message_future.hpp"
namespace cppa { namespace cppa {
...@@ -74,7 +76,7 @@ local_actor::local_actor(bool sflag) ...@@ -74,7 +76,7 @@ local_actor::local_actor(bool sflag)
, m_is_scheduled(sflag), m_dummy_node(), m_current_node(&m_dummy_node) { } , m_is_scheduled(sflag), m_dummy_node(), m_current_node(&m_dummy_node) { }
void local_actor::monitor(actor_ptr whom) { 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) { void local_actor::demonitor(actor_ptr whom) {
...@@ -111,7 +113,7 @@ void local_actor::reply_message(any_tuple&& what) { ...@@ -111,7 +113,7 @@ void local_actor::reply_message(any_tuple&& what) {
} }
auto& id = m_current_node->mid; auto& id = m_current_node->mid;
if (id.valid() == false || id.is_response()) { 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()) { else if (!id.is_answered()) {
if (chaining_enabled()) { if (chaining_enabled()) {
...@@ -149,7 +151,7 @@ response_handle local_actor::make_response_handle() { ...@@ -149,7 +151,7 @@ response_handle local_actor::make_response_handle() {
return std::move(result); 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, const util::duration& rel_time,
any_tuple&& what) { any_tuple&& what) {
auto mid = this->send_sync_message(whom, std::move(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) { ...@@ -166,4 +168,9 @@ sync_handle_helper local_actor::handle_response(const message_future& mf) {
return ::cppa::handle_response(mf); return ::cppa::handle_response(mf);
} }
void local_actor::cleanup(std::uint32_t reason) {
m_subscriptions.clear();
super::cleanup(reason);
}
} // namespace cppa } // namespace cppa
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <fstream> #include <fstream>
#include <algorithm> #include <algorithm>
#include <execinfo.h> //DEL_ME
#ifndef CPPA_WINDOWS #ifndef CPPA_WINDOWS
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
...@@ -70,11 +72,11 @@ class logging_impl : public logging { ...@@ -70,11 +72,11 @@ class logging_impl : public logging {
void initialize() { void initialize() {
m_thread = thread([this] { (*this)(); }); m_thread = thread([this] { (*this)(); });
log("TRACE ", "logging", "run", __FILE__, __LINE__, "ENTRY"); log("TRACE", "logging", "run", __FILE__, __LINE__, "ENTRY");
} }
void destroy() { void destroy() {
log("TRACE ", "logging", "run", __FILE__, __LINE__, "EXIT"); log("TRACE", "logging", "run", __FILE__, __LINE__, "EXIT");
// an empty string means: shut down // an empty string means: shut down
m_queue.push_back(new log_event{0, ""}); m_queue.push_back(new log_event{0, ""});
m_thread.join(); m_thread.join();
...@@ -101,18 +103,19 @@ class logging_impl : public logging { ...@@ -101,18 +103,19 @@ class logging_impl : public logging {
const char* function_name, const char* function_name,
const char* c_full_file_name, const char* c_full_file_name,
int line_num, int line_num,
const std::string &msg) { const std::string& msg) {
string class_name = c_class_name; string class_name = c_class_name;
replace_all(class_name, "::", "."); replace_all(class_name, "::", ".");
replace_all(class_name, "(anonymous namespace)", "$anon$");
string file_name; string file_name;
string full_file_name = c_full_file_name; string full_file_name = c_full_file_name;
auto i = find(full_file_name.rbegin(), full_file_name.rend(), '/').base(); auto ri = find(full_file_name.rbegin(), full_file_name.rend(), '/');
if (i == full_file_name.end()) { if (ri != full_file_name.rend()) {
file_name = move(full_file_name); auto i = ri.base();
} if (i == full_file_name.end()) file_name = move(full_file_name);
else { else file_name = string(i, full_file_name.end());
file_name = string(i, full_file_name.end());
} }
else file_name = move(full_file_name);
ostringstream line; ostringstream line;
line << time(0) << " " line << time(0) << " "
<< level << " " << level << " "
...@@ -134,9 +137,23 @@ class logging_impl : public logging { ...@@ -134,9 +137,23 @@ class logging_impl : public logging {
} // namespace <anonymous> } // 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::trace_helper::~trace_helper() {
get_logger()->log("TRACE", m_class.c_str(), m_fun_name,
m_file_name, m_line_num, "EXIT");
}
logging* logging::instance() { return detail::singleton_manager::get_logger(); } logging::~logging() { }
logging* logging::create_singleton() { return new logging_impl; } logging* logging::create_singleton() { return new logging_impl; }
......
...@@ -427,14 +427,11 @@ class middleman_impl : public abstract_middleman { ...@@ -427,14 +427,11 @@ class middleman_impl : public abstract_middleman {
} }
void run_later(function<void()> fun) { void run_later(function<void()> fun) {
CPPA_LOG_TRACE("");
m_queue.enqueue(new middleman_event(move(fun))); m_queue.enqueue(new middleman_event(move(fun)));
atomic_thread_fence(memory_order_seq_cst); atomic_thread_fence(memory_order_seq_cst);
uint8_t dummy = 0; uint8_t dummy = 0;
if (write(m_pipe_write, &dummy, sizeof(dummy)) != sizeof(dummy)) { // ignore result; write error only means middleman already exited
// already exited? static_cast<void>(write(m_pipe_write, &dummy, sizeof(dummy)));
CPPA_LOG_WARNING("cannot write to pipe");
}
} }
protected: protected:
...@@ -453,10 +450,9 @@ class middleman_impl : public abstract_middleman { ...@@ -453,10 +450,9 @@ class middleman_impl : public abstract_middleman {
void destroy() { void destroy() {
run_later([this] { run_later([this] {
CPPA_LOG_TRACE("lambda from middleman_impl::stop"); CPPA_LOGM_TRACE("destroy$helper", "");
this->m_done = true; this->m_done = true;
}); });
//enqueue_message(middleman_message::create());
m_thread.join(); m_thread.join();
close(m_pipe_read); close(m_pipe_read);
close(m_pipe_write); close(m_pipe_write);
......
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
namespace cppa { namespace detail { 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) { void object_array::push_back(const object& what) {
m_elements.push_back(what); m_elements.push_back(what);
......
...@@ -30,14 +30,11 @@ ...@@ -30,14 +30,11 @@
#include "cppa/detail/recursive_queue_node.hpp" #include "cppa/detail/recursive_queue_node.hpp"
using std::move;
namespace cppa { namespace detail { 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, any_tuple data,
message_id_t id) message_id_t id)
: next(nullptr), marked(false), sender(move(sptr)) : next(nullptr), marked(false), sender(sptr), msg(std::move(data)), mid(id) { }
, msg(move(data)), mid(id) { }
} } // namespace cppa::detail } } // namespace cppa::detail
...@@ -56,14 +56,23 @@ bool response_handle::synchronous() const { ...@@ -56,14 +56,23 @@ bool response_handle::synchronous() const {
void response_handle::apply(any_tuple msg) const { void response_handle::apply(any_tuple msg) const {
if (valid()) { if (valid()) {
local_actor* sptr = self.unchecked(); local_actor* sptr = self.unchecked();
if (sptr && sptr == m_from) { bool use_chaining = sptr && sptr == m_from && sptr->chaining_enabled();
if (sptr->chaining_enabled()) { if (synchronous()) {
if (m_to->chained_sync_enqueue(sptr, m_id, move(msg))) { if (use_chaining) {
if (m_to->chained_sync_enqueue(m_from, m_id, move(msg))) {
sptr->chained_actor(m_to); 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));
} }
} }
......
...@@ -28,13 +28,18 @@ ...@@ -28,13 +28,18 @@
\******************************************************************************/ \******************************************************************************/
#include "cppa/on.hpp"
#include "cppa/scheduler.hpp" #include "cppa/scheduler.hpp"
#include "cppa/scheduled_actor.hpp" #include "cppa/scheduled_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/detail/sync_request_bouncer.hpp"
namespace cppa { namespace cppa {
scheduled_actor::scheduled_actor(bool enable_chained_send) scheduled_actor::scheduled_actor(actor_state init_state, bool chained_send)
: local_actor(enable_chained_send), next(0), m_scheduler(0), m_hidden(false) { } : 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) { void scheduled_actor::attach_to_scheduler(scheduler* sched, bool hidden) {
CPPA_REQUIRE(sched != nullptr); CPPA_REQUIRE(sched != nullptr);
...@@ -50,8 +55,122 @@ void scheduled_actor::attach_to_scheduler(scheduler* sched, bool hidden) { ...@@ -50,8 +55,122 @@ void scheduled_actor::attach_to_scheduler(scheduler* sched, bool hidden) {
m_scheduler = sched; m_scheduler = sched;
} }
bool scheduled_actor::initialized() { bool scheduled_actor::initialized() const {
return m_scheduler != nullptr; 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 } // namespace cppa
...@@ -32,30 +32,26 @@ ...@@ -32,30 +32,26 @@
namespace cppa { namespace detail { namespace cppa { namespace detail {
scheduled_actor_dummy::scheduled_actor_dummy()
: scheduled_actor(actor_state::blocked, false) { }
void scheduled_actor_dummy::enqueue(const actor_ptr&, any_tuple) { }
void scheduled_actor_dummy::quit(std::uint32_t) { } void scheduled_actor_dummy::quit(std::uint32_t) { }
void scheduled_actor_dummy::dequeue(behavior&) { } void scheduled_actor_dummy::dequeue(behavior&) { }
void scheduled_actor_dummy::dequeue(partial_function&) { } void scheduled_actor_dummy::dequeue(partial_function&) { }
void scheduled_actor_dummy::dequeue_response(behavior&, message_id_t) { } 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::do_become(behavior&&, bool) { }
void scheduled_actor_dummy::become_waiting_for(behavior&&, message_id_t) { } void scheduled_actor_dummy::become_waiting_for(behavior&&, message_id_t) { }
bool scheduled_actor_dummy::has_behavior() { return false; } 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&) { resume_result scheduled_actor_dummy::resume(util::fiber*,actor_ptr&) {
return resume_result::actor_blocked; 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() { scheduled_actor_type scheduled_actor_dummy::impl_type() {
return event_based_impl; return event_based_impl;
} }
......
...@@ -36,13 +36,15 @@ ...@@ -36,13 +36,15 @@
#include "cppa/on.hpp" #include "cppa/on.hpp"
#include "cppa/self.hpp" #include "cppa/self.hpp"
#include "cppa/receive.hpp" #include "cppa/receive.hpp"
#include "cppa/logging.hpp"
#include "cppa/anything.hpp" #include "cppa/anything.hpp"
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/scheduler.hpp" #include "cppa/scheduler.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/thread_mapped_actor.hpp" #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/singleton_manager.hpp"
#include "cppa/detail/thread_pool_scheduler.hpp" #include "cppa/detail/thread_pool_scheduler.hpp"
...@@ -52,49 +54,29 @@ namespace cppa { namespace { ...@@ -52,49 +54,29 @@ namespace cppa { namespace {
typedef std::uint32_t ui32; typedef std::uint32_t ui32;
typedef std::chrono::high_resolution_clock hrc;
struct exit_observer : cppa::attachable { struct exit_observer : cppa::attachable {
~exit_observer() { ~exit_observer() { get_actor_registry()->dec_running(); }
cppa::detail::dec_actor_count(); void actor_exited(std::uint32_t) { }
} bool matches(const token&) { return false; }
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 { class delayed_msg {
public: public:
delayed_msg(async_send_fun arg0, delayed_msg(const channel_ptr& arg0,
const channel_ptr& arg1, const actor_ptr& arg1,
const actor_ptr& arg2,
message_id_t, message_id_t,
any_tuple&& arg4) any_tuple&& arg3)
: fun(arg0), ptr_a(arg1), from(arg2), msg(move(arg4)) { } : 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& arg1,
const actor_ptr& arg2, message_id_t arg2,
message_id_t arg3, any_tuple&& arg3)
any_tuple&& arg4) : ptr_b(arg0), from(arg1), id(arg2), msg(move(arg3)) { }
: fun(arg0), ptr_b(arg1), from(arg2), id(arg3), msg(move(arg4)) { }
delayed_msg(delayed_msg&&) = default; delayed_msg(delayed_msg&&) = default;
delayed_msg(const delayed_msg&) = default; delayed_msg(const delayed_msg&) = default;
...@@ -102,14 +84,13 @@ class delayed_msg { ...@@ -102,14 +84,13 @@ class delayed_msg {
delayed_msg& operator=(const delayed_msg&) = default; delayed_msg& operator=(const delayed_msg&) = default;
inline void eval() { inline void eval() {
if (fun.is_left()) (fun.left())(ptr_a.get(), from.get(), move(msg)); CPPA_REQUIRE(ptr_a || ptr_b);
else (fun.right())(ptr_b.get(), from.get(), id, move(msg)); if (ptr_a) ptr_a->enqueue(from, move(msg));
else ptr_b->sync_enqueue(from, id, move(msg));
} }
private: private:
either<async_send_fun, sync_reply_fun> fun;
channel_ptr ptr_a; channel_ptr ptr_a;
actor_ptr ptr_b; actor_ptr ptr_b;
actor_ptr from; actor_ptr from;
...@@ -127,14 +108,14 @@ class scheduler_helper { ...@@ -127,14 +108,14 @@ class scheduler_helper {
typedef intrusive_ptr<thread_mapped_actor> ptr_type; typedef intrusive_ptr<thread_mapped_actor> ptr_type;
void start() { void start() {
ptr_type mtimer{detail::memory::create<thread_mapped_actor>()}; ptr_type mt{detail::memory::create<thread_mapped_actor>()};
ptr_type mprinter{detail::memory::create<thread_mapped_actor>()}; ptr_type mp{detail::memory::create<thread_mapped_actor>()};
// launch threads // launch threads
m_timer_thread = std::thread(&scheduler_helper::timer_loop, mtimer); m_timer_thread = std::thread{&scheduler_helper::timer_loop, mt};
m_printer_thread = std::thread(&scheduler_helper::printer_loop, mprinter); m_printer_thread = std::thread{&scheduler_helper::printer_loop, mp};
// set member variables // set member variables
m_timer = mtimer; m_timer = std::move(mt);
m_printer = mprinter; m_printer = std::move(mp);
} }
void stop() { void stop() {
...@@ -159,47 +140,41 @@ class scheduler_helper { ...@@ -159,47 +140,41 @@ class scheduler_helper {
}; };
template<class Map, typename Fun, class T> template<class Map, class T>
void insert_dmsg(Map& storage, inline void insert_dmsg(Map& storage,
const util::duration& d, const util::duration& d,
Fun fun_ptr,
const T& to, const T& to,
const actor_ptr& sender, const actor_ptr& sender,
message_id_t id, any_tuple&& tup,
any_tuple&& tup ) { message_id_t id = message_id_t{}) {
auto tout = now(); auto tout = hrc::now();
tout += d; 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))); storage.insert(std::make_pair(std::move(tout), std::move(dmsg)));
} }
void scheduler_helper::timer_loop(scheduler_helper::ptr_type m_self) { 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 // setup & local variables
self.set(m_self.get()); 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; 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 // message handling rules
auto mfun = ( auto mfun = (
on(atom("SEND"), arg_match) >> [&](const util::duration& d, on(atom("SEND"), arg_match) >> [&](const util::duration& d,
const channel_ptr& ptr, const channel_ptr& ptr,
any_tuple& tup) { any_tuple& tup) {
insert_dmsg(messages, d, async_send_impl, insert_dmsg(messages, d, ptr, msg_ptr->sender, move(tup));
ptr, msg_ptr->sender, message_id_t(), move(tup));
}, },
on(atom("REPLY"), arg_match) >> [&](const util::duration& d, on(atom("REPLY"), arg_match) >> [&](const util::duration& d,
const actor_ptr& ptr, const actor_ptr& ptr,
message_id_t id, message_id_t id,
any_tuple& tup) { any_tuple& tup) {
insert_dmsg(messages, d, sync_reply_impl, insert_dmsg(messages, d, ptr, msg_ptr->sender, move(tup), id);
ptr, msg_ptr->sender, id, move(tup));
}, },
on<atom("DIE")>() >> [&]() { on(atom("DIE")) >> [&] {
done = true; done = true;
}, },
others() >> [&]() { others() >> [&]() {
...@@ -213,11 +188,9 @@ void scheduler_helper::timer_loop(scheduler_helper::ptr_type m_self) { ...@@ -213,11 +188,9 @@ void scheduler_helper::timer_loop(scheduler_helper::ptr_type m_self) {
// loop // loop
while (!done) { while (!done) {
while (!msg_ptr) { while (!msg_ptr) {
if (messages.empty()) { if (messages.empty()) msg_ptr.reset(queue.pop());
msg_ptr.reset(queue.pop());
}
else { else {
tout = now(); tout = hrc::now();
// handle timeouts (send messages) // handle timeouts (send messages)
auto it = messages.begin(); auto it = messages.begin();
while (it != messages.end() && (it->first) <= tout) { while (it != messages.end() && (it->first) <= tout) {
...@@ -301,6 +274,7 @@ void scheduler::initialize() { ...@@ -301,6 +274,7 @@ void scheduler::initialize() {
} }
void scheduler::destroy() { void scheduler::destroy() {
CPPA_LOG_TRACE("");
m_helper->stop(); m_helper->stop();
delete this; delete this;
} }
...@@ -309,19 +283,19 @@ scheduler::~scheduler() { ...@@ -309,19 +283,19 @@ scheduler::~scheduler() {
delete m_helper; delete m_helper;
} }
channel* scheduler::delayed_send_helper() { const actor_ptr& scheduler::delayed_send_helper() {
return m_helper->m_timer.get(); return m_helper->m_timer;
} }
void scheduler::register_converted_context(actor* what) { void scheduler::register_converted_context(actor* what) {
if (what) { if (what) {
detail::inc_actor_count(); get_actor_registry()->inc_running();
what->attach(new exit_observer); what->attach(attachable_ptr{new exit_observer});
} }
} }
attachable* scheduler::register_hidden_context() { attachable* scheduler::register_hidden_context() {
detail::inc_actor_count(); get_actor_registry()->inc_running();
return new exit_observer; return new exit_observer;
} }
...@@ -335,10 +309,6 @@ void set_default_scheduler(size_t num_threads) { ...@@ -335,10 +309,6 @@ void set_default_scheduler(size_t num_threads) {
set_scheduler(new detail::thread_pool_scheduler(num_threads)); set_scheduler(new detail::thread_pool_scheduler(num_threads));
} }
scheduler* get_scheduler() {
return detail::singleton_manager::get_scheduler();
}
scheduler* scheduler::create_singleton() { scheduler* scheduler::create_singleton() {
return new detail::thread_pool_scheduler; return new detail::thread_pool_scheduler;
} }
...@@ -347,5 +317,4 @@ const actor_ptr& scheduler::printer() const { ...@@ -347,5 +317,4 @@ const actor_ptr& scheduler::printer() const {
return m_helper->m_printer; return m_helper->m_printer;
} }
} // namespace cppa } // namespace cppa
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
#include "cppa/network/middleman.hpp" #include "cppa/network/middleman.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/empty_tuple.hpp" #include "cppa/detail/empty_tuple.hpp"
#include "cppa/detail/group_manager.hpp" #include "cppa/detail/group_manager.hpp"
#include "cppa/detail/actor_registry.hpp" #include "cppa/detail/actor_registry.hpp"
...@@ -77,16 +76,25 @@ void singleton_manager::shutdown() { ...@@ -77,16 +76,25 @@ void singleton_manager::shutdown() {
} }
//auto rptr = s_actor_registry.load(); //auto rptr = s_actor_registry.load();
//if (rptr) rptr->await_running_count_equal(0); //if (rptr) rptr->await_running_count_equal(0);
CPPA_LOGF_DEBUG("shutdown scheduler");
destroy(s_scheduler); destroy(s_scheduler);
CPPA_LOGF_DEBUG("shutdown middleman");
destroy(s_middleman); destroy(s_middleman);
std::atomic_thread_fence(std::memory_order_seq_cst); std::atomic_thread_fence(std::memory_order_seq_cst);
// it's safe to delete all other singletons now // it's safe to delete all other singletons now
CPPA_LOGF_DEBUG("close OpenCL command dispather");
destroy(s_command_dispatcher); destroy(s_command_dispatcher);
CPPA_LOGF_DEBUG("close actor registry");
destroy(s_actor_registry); destroy(s_actor_registry);
CPPA_LOGF_DEBUG("shutdown group manager");
destroy(s_group_manager); destroy(s_group_manager);
CPPA_LOGF_DEBUG("destroy empty tuple singleton");
destroy(s_empty_tuple); destroy(s_empty_tuple);
CPPA_LOGF_DEBUG("clear type info map");
destroy(s_uniform_type_info_map); destroy(s_uniform_type_info_map);
CPPA_LOGF_DEBUG("clear decorated names log");
destroy(s_decorated_names_map); destroy(s_decorated_names_map);
CPPA_LOGF_DEBUG("shutdown logger");
destroy(s_logger); destroy(s_logger);
} }
......
...@@ -43,35 +43,49 @@ ...@@ -43,35 +43,49 @@
namespace cppa { 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) thread_mapped_actor::thread_mapped_actor(std::function<void()> fun)
: super(std::move(fun)), m_initialized(false) { } : super(std::move(fun)), m_initialized(false) { }
void thread_mapped_actor::quit(std::uint32_t reason) { auto thread_mapped_actor::init_timeout(const util::duration& rel_time) -> timeout_type {
cleanup(reason); auto result = std::chrono::high_resolution_clock::now();
// actor_exited should not be catched, but if anyone does, result += rel_time;
// self must point to a newly created instance return result;
//self.set(nullptr);
throw actor_exited(reason);
} }
void thread_mapped_actor::enqueue(actor* sender, any_tuple msg) { void thread_mapped_actor::enqueue(const actor_ptr& sender, any_tuple msg) {
m_mailbox.push_back(fetch_node(sender, std::move(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, message_id_t id,
any_tuple msg ) { any_tuple msg) {
auto ptr = this->new_mailbox_element(sender, std::move(msg), id);
if (!m_mailbox.push_back(fetch_node(sender, std::move(msg), id))) { if (!this->m_mailbox.push_back(ptr)) {
detail::sync_request_bouncer f{this, exit_reason()}; detail::sync_request_bouncer f{this->exit_reason()};
f(sender, id); 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; 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 } // namespace cppa::detail
...@@ -34,11 +34,13 @@ ...@@ -34,11 +34,13 @@
#include <cstddef> #include <cstddef>
#include <iostream> #include <iostream>
#include "cppa/on.hpp"
#include "cppa/logging.hpp"
#include "cppa/event_based_actor.hpp" #include "cppa/event_based_actor.hpp"
#include "cppa/thread_mapped_actor.hpp" #include "cppa/thread_mapped_actor.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/context_switching_actor.hpp" #include "cppa/context_switching_actor.hpp"
#include "cppa/detail/actor_registry.hpp"
#include "cppa/detail/thread_pool_scheduler.hpp" #include "cppa/detail/thread_pool_scheduler.hpp"
using std::cout; using std::cout;
...@@ -46,13 +48,6 @@ using std::endl; ...@@ -46,13 +48,6 @@ using std::endl;
namespace cppa { namespace detail { namespace cppa { namespace detail {
namespace {
typedef std::unique_lock<std::mutex> guard_type;
typedef intrusive::single_reader_queue<thread_pool_scheduler::worker> worker_queue;
} // namespace <anonmyous>
struct thread_pool_scheduler::worker { struct thread_pool_scheduler::worker {
typedef scheduled_actor* job_ptr; typedef scheduled_actor* job_ptr;
...@@ -71,76 +66,63 @@ struct thread_pool_scheduler::worker { ...@@ -71,76 +66,63 @@ struct thread_pool_scheduler::worker {
worker& operator=(const worker&) = delete; worker& operator=(const worker&) = delete;
job_ptr aggressive_polling() { bool aggressive(job_ptr& result) {
job_ptr result = nullptr;
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
result = m_job_queue->try_pop(); result = m_job_queue->try_pop();
if (result) { if (result) return true;
return result;
}
std::this_thread::yield(); std::this_thread::yield();
} }
return result; return false;
} }
job_ptr less_aggressive_polling() { bool moderate(job_ptr& result) {
job_ptr result = nullptr;
for (int i = 0; i < 550; ++i) { for (int i = 0; i < 550; ++i) {
result = m_job_queue->try_pop(); result = m_job_queue->try_pop();
if (result) { if (result) return true;
return result;
}
//std::this_thread::sleep_for(std::chrono::milliseconds(1));
std::this_thread::sleep_for(std::chrono::microseconds(50)); std::this_thread::sleep_for(std::chrono::microseconds(50));
} }
return result; return false;
} }
job_ptr relaxed_polling() { bool relaxed(job_ptr& result) {
job_ptr result = nullptr;
for (;;) { for (;;) {
result = m_job_queue->try_pop(); result = m_job_queue->try_pop();
if (result) { if (result) return true;
return result;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
} }
void operator()() { void operator()() {
CPPA_LOG_TRACE("");
util::fiber fself; util::fiber fself;
job_ptr job = nullptr; job_ptr job = nullptr;
actor_ptr next_job; actor_ptr next;
for (;;) { for (;;) {
job = aggressive_polling(); aggressive(job) || moderate(job) || relaxed(job);
if (job == nullptr) { CPPA_LOG_DEBUG("dequeued new job");
job = less_aggressive_polling();
if (job == nullptr) {
job = relaxed_polling();
}
}
if (job == m_dummy) { if (job == m_dummy) {
CPPA_LOG_DEBUG("received dummy (quit)");
// dummy of doom received ... // dummy of doom received ...
m_job_queue->push_back(job); // kill the next guy m_job_queue->push_back(job); // kill the next guy
return; // and say goodbye return; // and say goodbye
} }
else {
do { do {
next_job.reset(); CPPA_LOG_DEBUG("resume actor with ID " << job->id());
if (job->resume(&fself, next_job) == resume_result::actor_done) { CPPA_REQUIRE(next == nullptr);
if (job->resume(&fself, next) == resume_result::actor_done) {
CPPA_LOG_DEBUG("actor is done");
bool hidden = job->is_hidden(); bool hidden = job->is_hidden();
job->deref(); job->deref();
//std::atomic_thread_fence(std::memory_order_seq_cst); if (!hidden) get_actor_registry()->dec_running();
if (!hidden) dec_actor_count();
} }
if (next_job) { if (next) {
job = static_cast<job_ptr>(next_job.get()); CPPA_LOG_DEBUG("got new job trough chaining");
//get_scheduler()->printer()->enqueue(job, make_any_tuple("fast-forwarded execution (chained actor)\n")); job = static_cast<job_ptr>(next.get());
next.reset();
} }
else job = nullptr; else job = nullptr;
} }
while (job); // loops until next_job was nullptr while (job); // loops until next == nullptr
}
} }
} }
...@@ -151,14 +133,13 @@ void thread_pool_scheduler::worker_loop(thread_pool_scheduler::worker* w) { ...@@ -151,14 +133,13 @@ void thread_pool_scheduler::worker_loop(thread_pool_scheduler::worker* w) {
} }
thread_pool_scheduler::thread_pool_scheduler() { thread_pool_scheduler::thread_pool_scheduler() {
m_num_threads = std::max<size_t>(std::thread::hardware_concurrency() * 2, 4); m_num_threads = std::max<size_t>(std::thread::hardware_concurrency()*2, 4);
} }
thread_pool_scheduler::thread_pool_scheduler(size_t num_worker_threads) { thread_pool_scheduler::thread_pool_scheduler(size_t num_worker_threads) {
m_num_threads = num_worker_threads; m_num_threads = num_worker_threads;
} }
void thread_pool_scheduler::supervisor_loop(job_queue* jqueue, void thread_pool_scheduler::supervisor_loop(job_queue* jqueue,
scheduled_actor* dummy, scheduled_actor* dummy,
size_t num_threads) { size_t num_threads) {
...@@ -180,17 +161,20 @@ void thread_pool_scheduler::initialize() { ...@@ -180,17 +161,20 @@ void thread_pool_scheduler::initialize() {
} }
void thread_pool_scheduler::destroy() { void thread_pool_scheduler::destroy() {
CPPA_LOG_TRACE("");
m_queue.push_back(&m_dummy); m_queue.push_back(&m_dummy);
CPPA_LOG_DEBUG("join supervisor");
m_supervisor.join(); m_supervisor.join();
// make sure job queue is empty, because destructor of m_queue would // make sure job queue is empty, because destructor of m_queue would
// otherwise delete elements it shouldn't // otherwise delete elements it shouldn't
CPPA_LOG_DEBUG("flush queue");
auto ptr = m_queue.try_pop(); auto ptr = m_queue.try_pop();
while (ptr != nullptr) { while (ptr != nullptr) {
if (ptr != &m_dummy) { if (ptr != &m_dummy) {
bool hidden = ptr->is_hidden(); bool hidden = ptr->is_hidden();
ptr->deref(); ptr->deref();
std::atomic_thread_fence(std::memory_order_seq_cst); 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(); ptr = m_queue.try_pop();
} }
...@@ -201,104 +185,71 @@ void thread_pool_scheduler::enqueue(scheduled_actor* what) { ...@@ -201,104 +185,71 @@ void thread_pool_scheduler::enqueue(scheduled_actor* what) {
m_queue.push_back(what); m_queue.push_back(what);
} }
actor_ptr thread_pool_scheduler::spawn_as_thread(void_function fun, template<typename F>
init_callback cb, void exec_as_thread(bool is_hidden, local_actor_ptr p, F f) {
bool hidden) { if (!is_hidden) get_actor_registry()->inc_running();
if (!hidden) inc_actor_count(); std::thread([=] {
thread_mapped_actor_ptr ptr{detail::memory::create<thread_mapped_actor>(std::move(fun))}; scoped_self_setter sss(p.get());
ptr->init(); try { f(); }
ptr->initialized(true);
cb(ptr.get());
std::thread([hidden, ptr]() {
scoped_self_setter sss{ptr.get()};
try {
ptr->run();
ptr->on_exit();
}
catch (...) { } catch (...) { }
if (!is_hidden) {
std::atomic_thread_fence(std::memory_order_seq_cst); std::atomic_thread_fence(std::memory_order_seq_cst);
if (!hidden) dec_actor_count(); get_actor_registry()->dec_running();
}).detach();
return ptr;
}
actor_ptr thread_pool_scheduler::spawn_impl(scheduled_actor_ptr what) {
if (what->has_behavior()) {
if (!what->is_hidden()) { inc_actor_count(); }
what->ref();
// event-based actors are not pushed to the job queue on startup
if (what->impl_type() == context_switching_impl) {
m_queue.push_back(what.get());
} }
} }).detach();
else {
what->on_exit();
}
return std::move(what);
} }
actor_ptr thread_pool_scheduler::spawn(scheduled_actor* raw, actor_ptr thread_pool_scheduler::exec(spawn_options os, scheduled_actor_ptr p) {
scheduling_hint hint) { CPPA_REQUIRE(p != nullptr);
scheduled_actor_ptr ptr{raw}; bool is_hidden = has_hide_flag(os);
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden); if (has_detach_flag(os)) {
return spawn_impl(std::move(ptr)); exec_as_thread(is_hidden, p, [p] {
p->exec_behavior_stack();
p->on_exit();
});
return std::move(p);
}
p->attach_to_scheduler(this, is_hidden);
if (p->has_behavior()) {
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: {
p->enqueue(nullptr, make_any_tuple(atom("RUN")));
break;
}
case context_switching_impl: {
m_queue.push_back(p.get());
break;
}
default: break; // nothing to do
}
}
else p->on_exit();
return std::move(p);
} }
actor_ptr thread_pool_scheduler::spawn(scheduled_actor* raw, actor_ptr thread_pool_scheduler::exec(spawn_options os,
init_callback cb, init_callback cb,
scheduling_hint hint) { void_function f) {
scheduled_actor_ptr ptr{raw}; if (has_blocking_api_flag(os)) {
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden); # ifndef CPPA_DISABLE_CONTEXT_SWITCHING
cb(ptr.get()); if (!has_detach_flag(os)) {
return spawn_impl(std::move(ptr)); return exec(os,
} memory::create<context_switching_actor>(std::move(f)));
}
#ifndef CPPA_DISABLE_CONTEXT_SWITCHING # endif
thread_mapped_actor_ptr p;
actor_ptr thread_pool_scheduler::spawn(void_function fun, scheduling_hint hint) { p.reset(memory::create<thread_mapped_actor>(std::move(f)));
if (hint == scheduled || hint == scheduled_and_hidden) { exec_as_thread(has_hide_flag(os), p, [p] {
scheduled_actor_ptr ptr{detail::memory::create<context_switching_actor>(std::move(fun))}; p->run();
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden); p->on_exit();
return spawn_impl(std::move(ptr)); });
} return std::move(p);
else { }
return spawn_as_thread(std::move(fun), auto p = event_based_actor::from(std::move(f));
[](local_actor*) { }, if (cb) cb(p.get());
hint == detached_and_hidden); return exec(os, p);
}
}
actor_ptr thread_pool_scheduler::spawn(void_function fun,
init_callback init_cb,
scheduling_hint hint) {
if (hint == scheduled || hint == scheduled_and_hidden) {
scheduled_actor_ptr ptr{detail::memory::create<context_switching_actor>(std::move(fun))};
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden);
init_cb(ptr.get());
return spawn_impl(std::move(ptr));
}
else {
return spawn_as_thread(std::move(fun),
std::move(init_cb),
hint == detached_and_hidden);
}
}
#else // CPPA_DISABLE_CONTEXT_SWITCHING
actor_ptr thread_pool_scheduler::spawn(void_function what, scheduling_hint sh) {
return spawn_as_thread(std::move(what),
[](local_actor*) { },
sh == detached_and_hidden);
}
actor_ptr thread_pool_scheduler::spawn(void_function what,
init_callback init_cb,
scheduling_hint sh) {
return spawn_as_thread(std::move(what),
std::move(init_cb),
sh == detached_and_hidden);
} }
#endif
} } // namespace cppa::detail } } // namespace cppa::detail
...@@ -16,6 +16,7 @@ namespace { ...@@ -16,6 +16,7 @@ namespace {
size_t s_pongs = 0; size_t s_pongs = 0;
behavior ping_behavior(size_t num_pings) { behavior ping_behavior(size_t num_pings) {
CPPA_LOGF_TRACE("");
return ( return (
on(atom("pong"), arg_match) >> [num_pings](int value) { on(atom("pong"), arg_match) >> [num_pings](int value) {
CPPA_LOGF_ERROR_IF(!self->last_sender(), "last_sender() == nullptr"); CPPA_LOGF_ERROR_IF(!self->last_sender(), "last_sender() == nullptr");
...@@ -38,6 +39,7 @@ behavior ping_behavior(size_t num_pings) { ...@@ -38,6 +39,7 @@ behavior ping_behavior(size_t num_pings) {
} }
behavior pong_behavior() { behavior pong_behavior() {
CPPA_LOGF_TRACE("");
return ( return (
on<atom("ping"), int>() >> [](int value) { on<atom("ping"), int>() >> [](int value) {
//cout << to_string(self->last_dequeued()) << endl; //cout << to_string(self->last_dequeued()) << endl;
...@@ -67,11 +69,9 @@ void ping(size_t num_pings) { ...@@ -67,11 +69,9 @@ void ping(size_t num_pings) {
actor_ptr spawn_event_based_ping(size_t num_pings) { actor_ptr spawn_event_based_ping(size_t num_pings) {
CPPA_LOGF_TRACE("num_pings = " << num_pings); CPPA_LOGF_TRACE("num_pings = " << num_pings);
s_pongs = 0; s_pongs = 0;
return factory::event_based( return spawn([=] {
[num_pings] { become(ping_behavior(num_pings));
self->become(ping_behavior(num_pings)); });
}
).spawn();
} }
void pong(actor_ptr ping_actor) { void pong(actor_ptr ping_actor) {
......
...@@ -33,13 +33,12 @@ const char* cppa_strip_path(const char* fname) { ...@@ -33,13 +33,12 @@ const char* cppa_strip_path(const char* fname) {
} }
void cppa_unexpected_message(const char* fname, size_t line_num) { void cppa_unexpected_message(const char* fname, size_t line_num) {
CPPA_FORMAT_STR(std::cerr, fname, line_num, CPPA_PRINTERRC(fname, line_num,
"ERROR: unexpected message: " "unexpected message: " << to_string(self->last_dequeued()));
<< to_string(self->last_dequeued()));
} }
void cppa_unexpected_timeout(const char* fname, size_t line_num) { 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) { std::vector<std::string> split(const std::string& str, char delim) {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <type_traits> #include <type_traits>
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
#include "cppa/logging.hpp"
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/util/scope_guard.hpp" #include "cppa/util/scope_guard.hpp"
...@@ -19,16 +20,21 @@ const char* cppa_strip_path(const char* fname); ...@@ -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_message(const char* fname, size_t line_num);
void cppa_unexpected_timeout(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) \ #define CPPA_STREAMIFY(fname, line, message) \
stream << cppa_strip_path(fname) << ":" \ cppa_strip_path(fname) << ":" << cppa_fill4(line) << " " << message
<< cppa_fill4(line) << " " << message \
<< std::endl #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_PRINT(message) \ #define CPPA_PRINT(message) CPPA_PRINTC(__FILE__, __LINE__, message)
CPPA_FORMAT_STR(std::cout, __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) \ #define CPPA_PRINTERR(message) CPPA_PRINTERRC(__FILE__, __LINE__, message)
CPPA_FORMAT_STR(std::cerr, __FILE__, __LINE__, message)
template<typename T1, typename T2> template<typename T1, typename T2>
struct both_integral { struct both_integral {
...@@ -53,7 +59,7 @@ inline std::string cppa_stream_arg(const bool& value) { ...@@ -53,7 +59,7 @@ inline std::string cppa_stream_arg(const bool& value) {
} }
inline void cppa_passed(const char* fname, int line_number) { 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> template<typename V1, typename V2>
...@@ -61,11 +67,9 @@ inline void cppa_failed(const V1& v1, ...@@ -61,11 +67,9 @@ inline void cppa_failed(const V1& v1,
const V2& v2, const V2& v2,
const char* fname, const char* fname,
int line_number) { int line_number) {
CPPA_FORMAT_STR(std::cout, fname, line_number, CPPA_PRINTERRC(fname, line_number,
"ERROR: expected value: " "expected value: " << cppa_stream_arg(v2)
<< cppa_stream_arg(v2) << ", found: " << cppa_stream_arg(v1));
<< ", found: "
<< cppa_stream_arg(v1));
cppa_inc_error_count(); cppa_inc_error_count();
} }
......
...@@ -62,7 +62,6 @@ struct replier : public event_based_actor { ...@@ -62,7 +62,6 @@ struct replier : public event_based_actor {
// receive seven reply messages (2 local, 5 remote) // receive seven reply messages (2 local, 5 remote)
void spawn5_server(actor_ptr client, bool inverted) { void spawn5_server(actor_ptr client, bool inverted) {
auto default_case = others() >> CPPA_UNEXPECTED_MSG_CB();
group_ptr grp; group_ptr grp;
if (!inverted) { if (!inverted) {
grp = group::get("local", "foobar"); grp = group::get("local", "foobar");
...@@ -77,43 +76,42 @@ void spawn5_server(actor_ptr client, bool inverted) { ...@@ -77,43 +76,42 @@ void spawn5_server(actor_ptr client, bool inverted) {
} }
spawn_in_group<reflector>(grp); spawn_in_group<reflector>(grp);
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) { on(atom("ok"), arg_match) >> [&](const actor_vector& vec) {
send(grp, "Hello reflectors!", 5.0); send(grp, "Hello reflectors!", 5.0);
if (vec.size() != 5) { if (vec.size() != 5) {
CPPA_PRINT("remote client did not spawn five reflectors!"); CPPA_PRINTERR("remote client did not spawn five reflectors!");
}
for (auto& a : vec) {
self->monitor(a);
} }
for (auto& a : vec) self->monitor(a);
}, },
default_case, others() >> CPPA_UNEXPECTED_MSG_CB(),
after(chrono::seconds(10)) >> [&] { after(chrono::seconds(10)) >> CPPA_UNEXPECTED_TOUT_CB()
throw runtime_error("timeout");
}
); );
CPPA_PRINT("wait for reflected messages"); CPPA_PRINT("wait for reflected messages");
// receive seven reply messages (2 local, 5 remote) // receive seven reply messages (2 local, 5 remote)
int x = 0; int x = 0;
receive_for(x, 7) ( 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"); CPPA_PRINT("wait for DOWN messages");
// wait for DOWN messages // wait for DOWN messages
{int i = 0; receive_for(i, 5) ( {int i = 0; receive_for(i, 5) (
on(atom("DOWN"), arg_match) >> [](std::uint32_t reason) { on(atom("DOWN"), arg_match) >> [](std::uint32_t reason) {
if (reason != exit_reason::normal) { 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)) >> [&] { after(chrono::seconds(2)) >> [&] {
i = 4; i = 4;
CPPA_PRINTERR("timeout while waiting for DOWN messages!"); CPPA_UNEXPECTED_TOUT();
} }
);} );}
CPPA_CHECKPOINT();
// wait for locally spawned reflectors // wait for locally spawned reflectors
await_all_others_done(); await_all_others_done();
CPPA_CHECKPOINT();
send(client, atom("Spawn5Done")); send(client, atom("Spawn5Done"));
} }
...@@ -157,11 +155,11 @@ int client_part(const vector<string_pair>& args) { ...@@ -157,11 +155,11 @@ int client_part(const vector<string_pair>& args) {
send(server, atom("SpawnPing")); send(server, atom("SpawnPing"));
receive ( receive (
on(atom("PingPtr"), arg_match) >> [](actor_ptr ping_actor) { on(atom("PingPtr"), arg_match) >> [](actor_ptr ping_actor) {
spawn<detached>(pong, ping_actor); spawn<detached + blocking_api>(pong, ping_actor);
} }
); );
await_all_others_done(); await_all_others_done();
receive_response (sync_send(server, atom("SyncMsg"))) ( sync_send(server, atom("SyncMsg")).await(
others() >> [&] { others() >> [&] {
if (self->last_dequeued() != make_cow_tuple(atom("SyncReply"))) { if (self->last_dequeued() != make_cow_tuple(atom("SyncReply"))) {
ostringstream oss; ostringstream oss;
...@@ -189,10 +187,8 @@ int client_part(const vector<string_pair>& args) { ...@@ -189,10 +187,8 @@ int client_part(const vector<string_pair>& args) {
); );
// test 100 sync_messages // test 100 sync_messages
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
receive_response (sync_send(server, atom("foo"), atom("bar"), i)) ( sync_send(server, atom("foo"), atom("bar"), i).await(
on(atom("foo"), atom("bar"), i) >> [] { on(atom("foo"), atom("bar"), i) >> [] { },
},
others() >> [&] { others() >> [&] {
CPPA_ERROR("unexpected message; " CPPA_ERROR("unexpected message; "
<< __FILE__ << " line " << __LINE__ << ": " << __FILE__ << " line " << __LINE__ << ": "
...@@ -203,20 +199,28 @@ int client_part(const vector<string_pair>& args) { ...@@ -203,20 +199,28 @@ int client_part(const vector<string_pair>& args) {
} }
); );
} }
CPPA_CHECKPOINT();
spawn5_server(server, false); spawn5_server(server, false);
CPPA_CHECKPOINT();
spawn5_client(); spawn5_client();
CPPA_CHECKPOINT();
// wait for locally spawned reflectors // wait for locally spawned reflectors
await_all_others_done(); await_all_others_done();
CPPA_CHECKPOINT();
receive ( receive (
on(atom("fwd"), arg_match) >> [&](const actor_ptr& fwd, const string&) { on(atom("fwd"), arg_match) >> [&](const actor_ptr& fwd, const string&) {
forward_to(fwd); forward_to(fwd);
} }
); );
CPPA_CHECKPOINT();
// shutdown handshake // shutdown handshake
send(server, atom("farewell")); send(server, atom("farewell"));
CPPA_CHECKPOINT();
receive(on(atom("cu")) >> [] { }); receive(on(atom("cu")) >> [] { });
CPPA_CHECKPOINT();
shutdown(); shutdown();
CPPA_CHECKPOINT();
return CPPA_TEST_RESULT(); return CPPA_TEST_RESULT();
} }
...@@ -251,7 +255,6 @@ int main(int argc, char** argv) { ...@@ -251,7 +255,6 @@ int main(int argc, char** argv) {
} }
} }
CPPA_TEST(test__remote_actor); CPPA_TEST(test__remote_actor);
CPPA_LOGF_TRACE("");
//auto ping_actor = spawn(ping, 10); //auto ping_actor = spawn(ping, 10);
uint16_t port = 4242; uint16_t port = 4242;
bool success = false; bool success = false;
...@@ -270,11 +273,11 @@ int main(int argc, char** argv) { ...@@ -270,11 +273,11 @@ int main(int argc, char** argv) {
thread child; thread child;
ostringstream oss; ostringstream oss;
if (run_remote_actor) { 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, // execute client_part() in a separate process,
// connected via localhost socket // connected via localhost socket
child = thread([&oss]() { child = thread([&oss]() {
CPPA_LOGF_TRACE("lambda expression calling system()"); CPPA_LOGC_TRACE("NONE", "main$thread_launcher", "");
string cmdstr = oss.str(); string cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) { if (system(cmdstr.c_str()) != 0) {
CPPA_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!"); CPPA_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!");
...@@ -282,9 +285,7 @@ int main(int argc, char** argv) { ...@@ -282,9 +285,7 @@ int main(int argc, char** argv) {
} }
}); });
} }
else { else { CPPA_PRINT("actor published at port " << port); }
CPPA_PRINT("actor published at port " << port);
}
//CPPA_PRINT("await SpawnPing message"); //CPPA_PRINT("await SpawnPing message");
actor_ptr remote_client; actor_ptr remote_client;
CPPA_LOGF_DEBUG("send 'SpawnPing', expect 'PingPtr'"); CPPA_LOGF_DEBUG("send 'SpawnPing', expect 'PingPtr'");
......
...@@ -121,10 +121,10 @@ class testee_actor { ...@@ -121,10 +121,10 @@ class testee_actor {
void wait4string() { void wait4string() {
bool string_received = false; bool string_received = false;
do_receive ( do_receive (
on<string>() >> [&]() { on<string>() >> [&] {
string_received = true; string_received = true;
}, },
on<atom("get_state")>() >> [&]() { on<atom("get_state")>() >> [&] {
reply("wait4string"); reply("wait4string");
} }
) )
...@@ -134,10 +134,10 @@ class testee_actor { ...@@ -134,10 +134,10 @@ class testee_actor {
void wait4float() { void wait4float() {
bool float_received = false; bool float_received = false;
do_receive ( do_receive (
on<float>() >> [&]() { on<float>() >> [&] {
float_received = true; float_received = true;
}, },
on<atom("get_state")>() >> [&]() { on<atom("get_state")>() >> [&] {
reply("wait4float"); reply("wait4float");
} }
) )
...@@ -149,10 +149,10 @@ class testee_actor { ...@@ -149,10 +149,10 @@ class testee_actor {
void operator()() { void operator()() {
receive_loop ( receive_loop (
on<int>() >> [&]() { on<int>() >> [&] {
wait4float(); wait4float();
}, },
on<atom("get_state")>() >> [&]() { on<atom("get_state")>() >> [&] {
reply("wait4int"); reply("wait4int");
} }
); );
...@@ -162,33 +162,19 @@ class testee_actor { ...@@ -162,33 +162,19 @@ class testee_actor {
// receives one timeout and quits // receives one timeout and quits
void testee1() { void testee1() {
receive ( become(after(chrono::milliseconds(10)) >> [] { unbecome(); });
after(chrono::milliseconds(10)) >> []() { }
);
} }
void testee2(actor_ptr other) { void testee2(actor_ptr other) {
self->link_to(other); self->link_to(other);
send(other, uint32_t(1)); send(other, uint32_t(1));
receive_loop ( become (
on<uint32_t>() >> [](uint32_t sleep_time) { on<uint32_t>() >> [](uint32_t sleep_time) {
// "sleep" for sleep_time milliseconds // "sleep" for sleep_time milliseconds
receive(after(chrono::milliseconds(sleep_time)) >> []() {}); become (
//reply(sleep_time * 2); keep_behavior,
} after(chrono::milliseconds(sleep_time)) >> [] { unbecome(); }
); );
}
void testee3(actor_ptr parent) {
// test a delayed_send / delayed_reply based loop
delayed_send(self, chrono::milliseconds(50), atom("Poll"));
int polls = 0;
receive_for(polls, 5) (
on(atom("Poll")) >> [&]() {
if (polls < 4) {
delayed_reply(chrono::milliseconds(50), atom("Poll"));
}
send(parent, atom("Push"), polls);
} }
); );
} }
...@@ -277,9 +263,10 @@ class fixed_stack : public sb_actor<fixed_stack> { ...@@ -277,9 +263,10 @@ class fixed_stack : public sb_actor<fixed_stack> {
}; };
void echo_actor() { void echo_actor() {
receive ( become (
others() >> []() { others() >> [] {
reply_tuple(self->last_dequeued()); reply_tuple(self->last_dequeued());
self->quit(exit_reason::normal);
} }
); );
} }
...@@ -315,12 +302,19 @@ int main() { ...@@ -315,12 +302,19 @@ int main() {
); );
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
auto mirror = spawn<simple_mirror>(); auto mirror = spawn<simple_mirror,monitored>();
CPPA_PRINT("test mirror"); CPPA_PRINT("test mirror");
send(mirror, "hello 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); 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(); await_all_others_done();
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
...@@ -423,7 +417,7 @@ int main() { ...@@ -423,7 +417,7 @@ int main() {
await_all_others_done(); await_all_others_done();
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
auto sync_testee1 = spawn([]() { auto sync_testee1 = spawn<blocking_api>([] {
receive ( receive (
on(atom("get")) >> []() { on(atom("get")) >> []() {
reply(42, 2); reply(42, 2);
...@@ -629,27 +623,25 @@ int main() { ...@@ -629,27 +623,25 @@ int main() {
}).spawn(); }).spawn();
await_all_others_done(); await_all_others_done();
auto res1 = behavior_test<testee_actor>(spawn(testee_actor{})); auto res1 = behavior_test<testee_actor>(spawn<blocking_api>(testee_actor{}));
CPPA_CHECK_EQUAL("wait4int", res1); CPPA_CHECK_EQUAL("wait4int", res1);
CPPA_CHECK_EQUAL(behavior_test<event_testee>(spawn<event_testee>()), "wait4int"); 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 // and kill them all through killing the link
auto twenty_thousand = spawn([]() { auto legion = spawn([] {
for (int i = 0; i < 20000; ++i) { CPPA_LOGF_INFO("spawn 1,000 actors");
spawn_link<event_testee>(); for (int i = 0; i < 1000; ++i) {
} spawn<event_testee,linked>();
receive_loop (
others() >> []() {
cout << "wtf? => " << to_string(self->last_dequeued()) << endl;
} }
); 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(); await_all_others_done();
CPPA_CHECKPOINT();
self->trap_exit(true); self->trap_exit(true);
auto ping_actor = spawn_monitor(ping, 10); auto ping_actor = spawn<monitored+blocking_api>(ping, 10);
auto pong_actor = spawn_monitor(pong, ping_actor); auto pong_actor = spawn<monitored+blocking_api>(pong, ping_actor);
self->link_to(pong_actor); self->link_to(pong_actor);
int i = 0; int i = 0;
int flags = 0; int flags = 0;
...@@ -665,11 +657,11 @@ int main() { ...@@ -665,11 +657,11 @@ int main() {
auto who = self->last_sender(); auto who = self->last_sender();
if (who == pong_actor) { if (who == pong_actor) {
flags |= 0x02; flags |= 0x02;
CPPA_CHECK_EQUAL(exit_reason::user_defined, reason); CPPA_CHECK_EQUAL(reason, exit_reason::user_defined);
} }
else if (who == ping_actor) { else if (who == ping_actor) {
flags |= 0x04; flags |= 0x04;
CPPA_CHECK_EQUAL(exit_reason::normal, reason); CPPA_CHECK_EQUAL(reason, exit_reason::normal);
} }
}, },
on_arg_match >> [&](const atom_value& val) { on_arg_match >> [&](const atom_value& val) {
......
...@@ -109,14 +109,13 @@ struct C : sb_actor<C> { ...@@ -109,14 +109,13 @@ struct C : sb_actor<C> {
\******************************************************************************/ \******************************************************************************/
struct D : popular_actor { struct D : popular_actor {
response_handle m_handle;
D(const actor_ptr& buddy) : popular_actor(buddy) { } D(const actor_ptr& buddy) : popular_actor(buddy) { }
void init() { void init() {
become ( become (
others() >> [=] { others() >> [=] {
m_handle = make_response_handle(); response_handle handle = make_response_handle();
sync_send_tuple(buddy(), last_dequeued()).then([=] { sync_send_tuple(buddy(), last_dequeued()).then([=] {
m_handle.apply(last_dequeued()); reply_to(handle, last_dequeued());
quit(); quit();
}); });
} }
...@@ -171,9 +170,10 @@ int main() { ...@@ -171,9 +170,10 @@ int main() {
self->on_sync_failure([] { self->on_sync_failure([] {
CPPA_ERROR("received: " << to_string(self->last_dequeued())); CPPA_ERROR("received: " << to_string(self->last_dequeued()));
}); });
spawn_monitor([&] { spawn<monitored + blocking_api>([] {
CPPA_LOGC_TRACE("NONE", "main$sync_failure_test", "id = " << self->id());
int invocations = 0; int invocations = 0;
auto foi = spawn_link<float_or_int>(); auto foi = spawn<float_or_int, linked>();
send(foi, atom("i")); send(foi, atom("i"));
receive(on_arg_match >> [](int i) { CPPA_CHECK_EQUAL(i, 0); }); receive(on_arg_match >> [](int i) { CPPA_CHECK_EQUAL(i, 0); });
self->on_sync_failure([] { self->on_sync_failure([] {
...@@ -228,11 +228,11 @@ int main() { ...@@ -228,11 +228,11 @@ int main() {
} }
); );
}; };
send(spawn_monitor<A>(self), atom("go"), spawn<B>(spawn<C>())); send(spawn<A, monitored>(self), atom("go"), spawn<B>(spawn<C>()));
await_success_message(); await_success_message();
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
await_all_others_done(); await_all_others_done();
send(spawn_monitor<A>(self), atom("go"), spawn<D>(spawn<C>())); send(spawn<A, monitored>(self), atom("go"), spawn<D>(spawn<C>()));
await_success_message(); await_success_message();
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
await_all_others_done(); await_all_others_done();
...@@ -276,10 +276,10 @@ int main() { ...@@ -276,10 +276,10 @@ int main() {
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
// test use case 3 // test use case 3
spawn_monitor([] { // client spawn<monitored + blocking_api>([] { // client
auto s = spawn_link<server>(); // server auto s = spawn<server, linked>(); // server
auto w = spawn_link([] { // worker auto w = spawn<linked>([] { // worker
receive_loop(on(atom("request")) >> []{ reply(atom("response")); }); become(on(atom("request")) >> []{ reply(atom("response")); });
}); });
// first 'idle', then 'request' // first 'idle', then 'request'
send_as(w, s, atom("idle")); send_as(w, s, atom("idle"));
......
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