Commit 8f7a794d authored by Dominik Charousset's avatar Dominik Charousset

improved become/unbecome API

parent 59f5dba9
......@@ -6,7 +6,6 @@ set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wextra -Wall -pedantic")
set(LIBCPPA_SRC
src/abstract_event_based_actor.cpp
src/abstract_tuple.cpp
src/actor.cpp
src/actor_count.cpp
......@@ -17,6 +16,7 @@ set(LIBCPPA_SRC
src/any_tuple.cpp
src/atom.cpp
src/attachable.cpp
src/behavior_stack.cpp
src/binary_deserializer.cpp
src/binary_serializer.cpp
src/channel.cpp
......@@ -52,7 +52,6 @@ set(LIBCPPA_SRC
src/serializer.cpp
src/shared_spinlock.cpp
src/singleton_manager.cpp
src/stacked_event_based_actor.cpp
src/string_serialization.cpp
src/thread_pool_scheduler.cpp
src/to_uniform_name.cpp
......
......@@ -18,7 +18,7 @@ benchmarks/theron_mailbox_performance.cpp
benchmarks/theron_mixed_case.cpp
benchmarks/utility.hpp
cppa/abstract_actor.hpp
cppa/abstract_event_based_actor.hpp
cppa/event_based_actor.hpp
cppa/actor.hpp
cppa/actor_proxy.hpp
cppa/announce.hpp
......@@ -93,8 +93,6 @@ cppa/detail/value_guard.hpp
cppa/detail/yield_interface.hpp
cppa/context_switching_actor.hpp
cppa/either.hpp
cppa/event_based_actor.hpp
cppa/event_based_actor_base.hpp
cppa/exception.hpp
cppa/exit_reason.hpp
cppa/from_string.hpp
......@@ -124,7 +122,6 @@ cppa/scheduler.hpp
cppa/scheduling_hint.hpp
cppa/self.hpp
cppa/serializer.hpp
cppa/stacked_event_based_actor.hpp
cppa/to_string.hpp
cppa/tpartial_function.hpp
cppa/tuple_cast.hpp
......@@ -190,7 +187,7 @@ queue_performances/intrusive_sutter_list.hpp
queue_performances/lockfree_list.hpp
queue_performances/main.cpp
queue_performances/sutter_list.hpp
src/abstract_event_based_actor.cpp
src/event_based_actor.cpp
src/abstract_tuple.cpp
src/actor.cpp
src/actor_count.cpp
......@@ -210,7 +207,6 @@ src/demangle.cpp
src/deserializer.cpp
src/duration.cpp
src/empty_tuple.cpp
src/event_based_actor.cpp
src/exception.cpp
src/fiber.cpp
src/group.cpp
......@@ -236,7 +232,6 @@ src/self.cpp
src/serializer.cpp
src/shared_spinlock.cpp
src/singleton_manager.cpp
src/stacked_event_based_actor.cpp
src/string_serialization.cpp
src/thread_pool_scheduler.cpp
src/to_uniform_name.cpp
......@@ -265,3 +260,5 @@ unit_testing/test__type_list.cpp
unit_testing/test__uniform_type.cpp
unit_testing/test__yield_interface.cpp
cppa/detail/receive_policy.hpp
cppa/detail/behavior_stack.hpp
src/behavior_stack.cpp
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011, 2012 *
* 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_EVENT_BASED_ACTOR_HPP
#define CPPA_ABSTRACT_EVENT_BASED_ACTOR_HPP
#include <stack>
#include <memory>
#include <vector>
#include "cppa/config.hpp"
#include "cppa/either.hpp"
#include "cppa/pattern.hpp"
#include "cppa/behavior.hpp"
#include "cppa/detail/disablable_delete.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa {
/**
* @brief Base class for all event-based actor implementations.
*/
class abstract_event_based_actor : public detail::abstract_scheduled_actor {
friend class detail::receive_policy;
typedef detail::abstract_scheduled_actor super;
public:
void dequeue(behavior&); //override
void dequeue(partial_function&); //override
resume_result resume(util::fiber*); //override
/**
* @brief Initializes the actor by defining an initial behavior.
*/
virtual void init() = 0;
/**
* @copydoc cppa::scheduled_actor::on_exit()
*/
virtual void on_exit();
protected:
inline behavior& current_behavior() {
CPPA_REQUIRE(m_behavior_stack.empty() == false);
return *(m_behavior_stack.back());
}
abstract_event_based_actor();
// ownership flag + pointer
typedef std::unique_ptr<behavior, detail::disablable_delete<behavior>>
stack_element;
std::vector<stack_element> m_behavior_stack;
std::vector<stack_element> m_erased_stack_elements;
detail::receive_policy m_recv_policy;
// provoke compiler errors for usage of receive() and related functions
/**
* @brief Provokes a compiler error to ensure that an event-based actor
* does not accidently uses receive() instead of become().
*/
template<typename... Args>
void receive(Args&&...) {
static_assert((sizeof...(Args) + 1) < 1,
"You shall not use receive in an event-based actor. "
"Use become() instead.");
}
/**
* @brief Provokes a compiler error.
*/
template<typename... Args>
void receive_loop(Args&&... args) {
receive(std::forward<Args>(args)...);
}
/**
* @brief Provokes a compiler error.
*/
template<typename... Args>
void receive_while(Args&&... args) {
receive(std::forward<Args>(args)...);
}
/**
* @brief Provokes a compiler error.
*/
template<typename... Args>
void do_receive(Args&&... args) {
receive(std::forward<Args>(args)...);
}
private:
// required by detail::nestable_receive_policy
static const detail::receive_policy_flag receive_flag = detail::rp_event_based;
inline void handle_timeout(behavior& bhvr) {
m_has_pending_timeout_request = false;
CPPA_REQUIRE(bhvr.timeout().valid());
bhvr.handle_timeout();
if (!m_behavior_stack.empty()) {
auto& next_bhvr = *(m_behavior_stack.back());
request_timeout(next_bhvr.timeout());
}
}
};
} // namespace cppa
#endif // CPPA_ABSTRACT_EVENT_BASED_ACTOR_HPP
......@@ -40,8 +40,9 @@
#include "cppa/either.hpp"
#include "cppa/pattern.hpp"
#include "cppa/detail/yield_interface.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/yield_interface.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa {
......@@ -83,12 +84,16 @@ class context_switching_actor : public detail::abstract_scheduled_actor {
resume_result resume(util::fiber* from); //override
void unbecome();
protected:
context_switching_actor();
virtual void run();
void do_become(behavior* bhvr, bool ownership, bool discard);
private:
// required by detail::nestable_receive_policy
......@@ -104,6 +109,7 @@ class context_switching_actor : public detail::abstract_scheduled_actor {
util::fiber m_fiber;
std::function<void()> m_behavior;
detail::receive_policy m_recv_policy;
std::unique_ptr<detail::behavior_stack> m_stack_ptr;
};
......
......@@ -131,7 +131,7 @@ class abstract_scheduled_actor : public abstract_actor<scheduled_actor> {
return enqueue_node(super::fetch_node(sender, std::move(msg)), pending);
}
void quit(std::uint32_t reason) {
void quit(std::uint32_t reason = exit_reason::normal) {
this->cleanup(reason);
throw actor_exited(reason);
}
......
......@@ -28,47 +28,52 @@
\******************************************************************************/
#ifndef CPPA_STACKED_EVENT_BASED_ACTOR_HPP
#define CPPA_STACKED_EVENT_BASED_ACTOR_HPP
#ifndef BEHAVIOR_STACK_HPP
#define BEHAVIOR_STACK_HPP
#include "cppa/event_based_actor_base.hpp"
#include <vector>
#include <memory>
namespace cppa {
#include "cppa/behavior.hpp"
#include "cppa/detail/disablable_delete.hpp"
#ifdef CPPA_DOCUMENTATION
namespace cppa { namespace detail {
/**
* @brief A base class for event-based actors using a behavior stack.
*/
class stacked_event_based_actor : public event_based_actor {
class behavior_stack
{
protected:
behavior_stack(const behavior_stack&) = delete;
behavior_stack& operator=(const behavior_stack&) = delete;
/**
* @brief Restores the last behavior.
*/
void unbecome();
public:
};
behavior_stack() = default;
#else
typedef detail::disablable_delete<behavior> deleter;
typedef std::unique_ptr<behavior, deleter> value_type;
class stacked_event_based_actor : public event_based_actor_base<stacked_event_based_actor> {
inline bool empty() const { return m_elements.empty(); }
friend class event_based_actor_base<stacked_event_based_actor>;
inline behavior& back() { return *m_elements.back(); }
typedef abstract_event_based_actor::stack_element stack_element;
// executes the behavior stack
void exec();
void do_become(behavior* behavior, bool has_ownership);
void pop_back();
protected:
void push_back(behavior* what, bool has_ownership);
void unbecome();
void cleanup();
};
void clear();
private:
#endif
std::vector<value_type> m_elements;
std::vector<value_type> m_erased_elements;
};
} // namespace cppa
} } // namespace cppa::detail
#endif // CPPA_STACKED_EVENT_BASED_ACTOR_HPP
#endif // BEHAVIOR_STACK_HPP
......@@ -43,8 +43,10 @@
namespace cppa { namespace detail {
enum receive_policy_flag {
// blocking message processing: thread-mapped & context-switching actors
rp_nestable,
rp_event_based
// callback-based message processing: event-based actors
rp_callback
};
class receive_policy {
......@@ -124,8 +126,8 @@ class receive_policy {
private:
typedef std::integral_constant<receive_policy_flag, rp_nestable> rp_n;
typedef std::integral_constant<receive_policy_flag, rp_event_based> rp_eb;
typedef std::integral_constant<receive_policy_flag, rp_nestable> nestable;
typedef std::integral_constant<receive_policy_flag, rp_callback> callback;
std::list<std::unique_ptr<recursive_queue_node> > m_cache;
......@@ -143,7 +145,7 @@ class receive_policy {
handle_message_result handle_message(Client* client,
recursive_queue_node& node,
FunOrBehavior& fun,
rp_n) {
nestable) {
if (node.marked) {
return hm_skip_msg;
}
......@@ -181,7 +183,7 @@ class receive_policy {
handle_message_result handle_message(Client* client,
recursive_queue_node& node,
FunOrBehavior& fun,
rp_eb) {
callback) {
CPPA_REQUIRE(node.marked == false);
switch (client->filter_msg(node.msg)) {
case normal_exit_signal:
......
......@@ -46,6 +46,8 @@ struct scheduled_actor_dummy : abstract_scheduled_actor {
bool remove_backlink(intrusive_ptr<actor>&);
void detach(const attachable::token&);
bool attach(attachable*);
void unbecome();
void do_become(behavior*, bool, bool);
};
} } // namespace cppa::detail
......
......@@ -28,61 +28,123 @@
\******************************************************************************/
#ifndef CPPA_EVENT_BASED_ACTOR_HPP
#define CPPA_EVENT_BASED_ACTOR_HPP
#ifndef CPPA_ABSTRACT_EVENT_BASED_ACTOR_HPP
#define CPPA_ABSTRACT_EVENT_BASED_ACTOR_HPP
#include <stack>
#include <memory>
#include <vector>
#include "cppa/config.hpp"
#include "cppa/either.hpp"
#include "cppa/pattern.hpp"
#include "cppa/behavior.hpp"
#include "cppa/event_based_actor_base.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/abstract_scheduled_actor.hpp"
namespace cppa {
#ifdef CPPA_DOCUMENTATION
/**
* @brief Base class for non-stacked event-based actor implementations.
* @brief Base class for all event-based actor implementations.
*/
class event_based_actor : public scheduled_actor {
#else // CPPA_DOCUMENTATION
class event_based_actor : public detail::abstract_scheduled_actor {
#endif // CPPA_DOCUMENTATION
protected:
friend class detail::receive_policy;
typedef detail::abstract_scheduled_actor super;
/**
* @brief Sets the actor's behavior to @p bhvr.
* @note @p bhvr is owned by the caller and must remain valid until
* the actor terminates.
* @note The recommended way of using this member function is to pass
* a pointer to a member variable.
*/
void become(behavior* bhvr);
public:
void dequeue(behavior&); //override
void dequeue(partial_function&); //override
resume_result resume(util::fiber*); //override
/**
* @brief Sets the actor's behavior.
* @brief Initializes the actor.
*/
template<typename Arg0, typename... Args>
void become(Arg0&& arg0, Args&&... args):
virtual void init() = 0;
};
virtual void on_exit();
void quit(std::uint32_t reason = exit_reason::normal);
#else
void unbecome();
class event_based_actor : public event_based_actor_base<event_based_actor> {
protected:
friend class event_based_actor_base<event_based_actor>;
event_based_actor();
typedef abstract_event_based_actor::stack_element stack_element;
// provoke compiler errors for usage of receive() and related functions
// has_ownership == false
void do_become(behavior* bhvr, bool has_ownership);
/**
* @brief Provokes a compiler error to ensure that an event-based actor
* does not accidently uses receive() instead of become().
*/
template<typename... Args>
void receive(Args&&...) {
static_assert((sizeof...(Args) + 1) < 1,
"You shall not use receive in an event-based actor. "
"Use become() instead.");
}
public:
/**
* @brief Provokes a compiler error.
*/
template<typename... Args>
void receive_loop(Args&&... args) {
receive(std::forward<Args>(args)...);
}
event_based_actor();
/**
* @brief Provokes a compiler error.
*/
template<typename... Args>
void receive_while(Args&&... args) {
receive(std::forward<Args>(args)...);
}
virtual void quit(std::uint32_t reason = exit_reason::normal);
/**
* @brief Provokes a compiler error.
*/
template<typename... Args>
void do_receive(Args&&... args) {
receive(std::forward<Args>(args)...);
}
private:
inline behavior& current_behavior() {
CPPA_REQUIRE(m_stack.empty() == false);
return m_stack.back();
}
// required by detail::nestable_receive_policy
static const detail::receive_policy_flag receive_flag = detail::rp_callback;
inline void handle_timeout(behavior& bhvr) {
CPPA_REQUIRE(bhvr.timeout().valid());
m_has_pending_timeout_request = false;
bhvr.handle_timeout();
if (m_stack.empty() == false) {
request_timeout(current_behavior().timeout());
}
}
void do_become(behavior* bhvr, bool has_ownership, bool discard_old_bhvr);
// stack elements are moved to m_erased_stack_elements and erased later
// to prevent possible segfaults that can occur if a currently executed
// lambda gets deleted
detail::behavior_stack m_stack;
detail::receive_policy m_recv_policy;
};
#endif
} // namespace cppa
#endif // CPPA_EVENT_BASED_ACTOR_HPP
#endif // CPPA_ABSTRACT_EVENT_BASED_ACTOR_HPP
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011, 2012 *
* 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_EVENT_BASED_ACTOR_BASE_HPP
#define CPPA_EVENT_BASED_ACTOR_BASE_HPP
#include "cppa/behavior.hpp"
#include "cppa/abstract_event_based_actor.hpp"
namespace cppa {
/**
* @brief Base class for event-based actor implementations.
*/
template<typename Derived>
class event_based_actor_base : public abstract_event_based_actor {
typedef abstract_event_based_actor super;
inline Derived* d_this() { return static_cast<Derived*>(this); }
protected:
/**
* @brief Sets the actor's behavior to @p bhvr.
* @note @p bhvr is owned by caller and must remain valid until
* the actor terminates.
* This member function should be used to use a member of
* a subclass as behavior.
*/
inline void become(behavior* bhvr) {
d_this()->do_become(bhvr, false);
}
/** @brief Sets the actor's behavior. */
template<typename Arg0, typename... Args>
void become(Arg0&& arg0, Args&&... args) {
behavior tmp = match_expr_concat(std::forward<Arg0>(arg0),
std::forward<Args>(args)...);
d_this()->do_become(new behavior(std::move(tmp)), true);
}
};
} // namespace cppa
#endif // CPPA_EVENT_BASED_ACTOR_BASE_HPP
......@@ -41,6 +41,32 @@
namespace cppa {
class scheduler;
class local_scheduler;
struct discard_behavior_t { };
struct keep_behavior_t { };
#ifndef CPPA_DOCUMENTATION
namespace {
#endif // CPPA_DOCUMENTATION
/**
* @brief Policy tag that causes {@link event_based_actor::become} to
* discard the current behavior.
* @relates event_based_actor
*/
constexpr discard_behavior_t discard_behavior = discard_behavior_t();
/**
* @brief Policy tag that causes {@link event_based_actor::become} to
* keep the current behavior available.
* @relates event_based_actor
*/
constexpr keep_behavior_t keep_behavior = keep_behavior_t();
#ifndef CPPA_DOCUMENTATION
} // namespace <anonymous>
#endif // CPPA_DOCUMENTATION
/**
* @brief Base class for local running Actors.
......@@ -149,6 +175,85 @@ class local_actor : public actor {
*/
void demonitor(actor_ptr whom);
// become/unbecome API
/**
* @brief Sets the actor's behavior to @p bhvr and discards the
* previous 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.
*/
inline void become(discard_behavior_t, behavior* bhvr) {
do_become(bhvr, false, true);
}
inline void become(discard_behavior_t, behavior&& bhvr) {
do_become(new behavior(std::move(bhvr)), true, true);
}
/**
* @brief Sets the actor's behavior to @p bhvr and keeps the
* previous behavior, so that it can be restored by calling
* {@link unbecome()}.
* @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.
*/
inline void become(keep_behavior_t, behavior* bhvr) {
do_become(bhvr, false, false);
}
inline void become(keep_behavior_t, behavior&& bhvr) {
do_become(new behavior(std::move(bhvr)), true, false);
}
inline void become(behavior&& bhvr) {
become(discard_behavior, std::move(bhvr));
}
/**
* @brief Equal to <tt>become(discard_old, bhvr)</tt>.
*/
inline void become(behavior* bhvr) {
become(discard_behavior, bhvr);
}
/**
* @brief Sets the actor's behavior.
*/
template<typename... Cases, typename... Args>
inline void become(discard_behavior_t, match_expr<Cases...>&& arg0, Args&&... args) {
become(discard_behavior, bhvr_collapse(std::move(arg0), std::forward<Args>(args)...));
}
/**
* @brief Sets the actor's behavior.
*/
template<typename... Cases, typename... Args>
inline void become(keep_behavior_t, match_expr<Cases...>&& arg0, Args&&... args) {
become(keep_behavior, bhvr_collapse(std::move(arg0), std::forward<Args>(args)...));
}
/**
* @brief Sets the actor's behavior. Equal to
* <tt>become(discard_old, arg0, args...)</tt>.
*/
template<typename Arg0, typename... Args>
void become(Arg0&& arg0, Args&&... args);
template<typename... Cases, typename... Args>
inline void become(match_expr<Cases...> arg0, Args&&... args) {
become(discard_behavior, bhvr_collapse(std::move(arg0), std::forward<Args>(args)...));
}
/**
* @brief Returns to a previous behavior or finishes execution
* if no previous behavior is available.
*/
virtual void unbecome() = 0;
// library-internal members and member functions that shall
// not appear in the documentation
......@@ -186,6 +291,10 @@ class local_actor : public actor {
# endif // CPPA_DOCUMENTATION
protected:
virtual void do_become(behavior* bhvr, bool ownership, bool discard) = 0;
};
/**
......
......@@ -51,8 +51,9 @@
#include "cppa/intrusive/singly_linked_list.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
#include "cppa/detail/receive_policy.hpp"
#include "cppa/detail/behavior_stack.hpp"
#include "cppa/detail/recursive_queue_node.hpp"
namespace cppa {
......@@ -74,7 +75,7 @@ class thread_mapped_actor : public abstract_actor<local_actor> {
public:
void quit(std::uint32_t reason); //override
void quit(std::uint32_t reason = exit_reason::normal); //override
void enqueue(actor* sender, any_tuple msg); //override
......@@ -86,9 +87,16 @@ class thread_mapped_actor : public abstract_actor<local_actor> {
inline decltype(m_mailbox)& mailbox() { return m_mailbox; }
void unbecome();
protected:
void do_become(behavior* bhvr, bool ownership, bool discard);
private:
detail::receive_policy m_recv_policy;
std::unique_ptr<detail::behavior_stack> m_stack_ptr;
// required by nestable_receive_policy
static const detail::receive_policy_flag receive_flag = detail::rp_nestable;
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011, 2012 *
* 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 <iostream>
#include "cppa/to_string.hpp"
#include "cppa/self.hpp"
#include "cppa/abstract_event_based_actor.hpp"
#include "cppa/detail/filter_result.hpp"
namespace cppa {
abstract_event_based_actor::abstract_event_based_actor()
: super(super::blocked) { }
void abstract_event_based_actor::dequeue(behavior&) {
quit(exit_reason::unallowed_function_call);
}
void abstract_event_based_actor::dequeue(partial_function&) {
quit(exit_reason::unallowed_function_call);
}
resume_result abstract_event_based_actor::resume(util::fiber*) {
auto done_cb = [&]() {
m_state.store(abstract_scheduled_actor::done);
m_behavior_stack.clear();
on_exit();
};
self.set(this);
try {
detail::recursive_queue_node* e;
for (;;) {
e = m_mailbox.try_pop();
if (!e) {
m_state.store(abstract_scheduled_actor::about_to_block);
if (m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state(abstract_scheduled_actor::about_to_block,
abstract_scheduled_actor::blocked)) {
case abstract_scheduled_actor::ready: {
break;
}
case abstract_scheduled_actor::blocked: {
return resume_result::actor_blocked;
}
default: CPPA_CRITICAL("illegal actor state");
};
}
}
else {
if (m_recv_policy.invoke(this, e, current_behavior())) {
// try to match cached message before receiving new ones
do {
m_erased_stack_elements.clear();
if (m_behavior_stack.empty()) {
done_cb();
return resume_result::actor_done;
}
} while (m_recv_policy.invoke_from_cache(this, current_behavior()));
}
}
}
}
catch (actor_exited& what) {
cleanup(what.reason());
}
catch (...) {
cleanup(exit_reason::unhandled_exception);
}
done_cb();
return resume_result::actor_done;
}
void abstract_event_based_actor::on_exit() {
}
} // namespace cppa
......@@ -28,24 +28,42 @@
\******************************************************************************/
#include "cppa/stacked_event_based_actor.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/detail/behavior_stack.hpp"
namespace cppa {
namespace cppa { namespace detail {
void stacked_event_based_actor::unbecome() {
if (m_behavior_stack.empty() == false) {
m_erased_stack_elements.emplace_back(std::move(m_behavior_stack.back()));
m_behavior_stack.pop_back();
// executes the behavior stack
void behavior_stack::exec() {
while (!empty()) {
self->dequeue(back());
cleanup();
}
}
void stacked_event_based_actor::do_become(behavior* bhvr,
bool has_ownership) {
reset_timeout();
request_timeout(bhvr->timeout());
stack_element se{bhvr};
if (!has_ownership) se.get_deleter().disable();
m_behavior_stack.push_back(std::move(se));
void behavior_stack::pop_back() {
if (m_elements.empty() == false) {
m_erased_elements.emplace_back(std::move(m_elements.back()));
m_elements.pop_back();
}
}
void behavior_stack::push_back(behavior* what, bool has_ownership) {
value_type new_element{what};
if (!has_ownership) new_element.get_deleter().disable();
m_elements.push_back(std::move(new_element));
}
void behavior_stack::clear() {
if (m_elements.empty() == false) {
std::move(m_elements.begin(), m_elements.end(),
std::back_inserter(m_erased_elements));
m_elements.clear();
}
}
void behavior_stack::cleanup() {
m_erased_elements.clear();
}
} // namespace cppa
} } // namespace cppa::detail
......@@ -145,6 +145,28 @@ resume_result context_switching_actor::resume(util::fiber* from) {
}
}
void context_switching_actor::unbecome() {
if (m_stack_ptr) {
m_stack_ptr->pop_back();
}
else {
quit();
}
}
void context_switching_actor::do_become(behavior* bhvr, bool ownership, bool discard) {
if (m_stack_ptr) {
if (discard) m_stack_ptr->pop_back();
m_stack_ptr->push_back(bhvr, ownership);
}
else {
m_stack_ptr.reset(new detail::behavior_stack);
m_stack_ptr->exec();
quit();
}
}
} // namespace cppa
#else // ifdef CPPA_DISABLE_CONTEXT_SWITCHING
......
......@@ -28,33 +28,100 @@
\******************************************************************************/
#include <iostream>
#include "cppa/to_string.hpp"
#include "cppa/self.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/detail/filter_result.hpp"
namespace cppa {
event_based_actor::event_based_actor() {
event_based_actor::event_based_actor() : super(super::blocked) { }
void event_based_actor::dequeue(behavior&) {
quit(exit_reason::unallowed_function_call);
}
void event_based_actor::dequeue(partial_function&) {
quit(exit_reason::unallowed_function_call);
}
resume_result event_based_actor::resume(util::fiber*) {
auto done_cb = [&]() {
m_state.store(abstract_scheduled_actor::done);
m_stack.clear();
on_exit();
};
self.set(this);
try {
detail::recursive_queue_node* e;
for (;;) {
e = m_mailbox.try_pop();
if (!e) {
m_state.store(abstract_scheduled_actor::about_to_block);
if (m_mailbox.can_fetch_more() == false) {
switch (compare_exchange_state(abstract_scheduled_actor::about_to_block,
abstract_scheduled_actor::blocked)) {
case abstract_scheduled_actor::ready: {
break;
}
case abstract_scheduled_actor::blocked: {
return resume_result::actor_blocked;
}
default: CPPA_CRITICAL("illegal actor state");
};
}
}
else {
if (m_recv_policy.invoke(this, e, current_behavior())) {
// try to match cached message before receiving new ones
do {
m_stack.cleanup();
if (m_stack.empty()) {
done_cb();
return resume_result::actor_done;
}
} while (m_recv_policy.invoke_from_cache(this, current_behavior()));
}
}
}
}
catch (actor_exited& what) {
cleanup(what.reason());
}
catch (...) {
cleanup(exit_reason::unhandled_exception);
}
done_cb();
return resume_result::actor_done;
}
void event_based_actor::do_become(behavior* bhvr,
bool has_ownership,
bool discard_old_bhvr) {
reset_timeout();
request_timeout(bhvr->timeout());
if (discard_old_bhvr) m_stack.pop_back();
m_stack.push_back(bhvr, has_ownership);
}
void event_based_actor::quit(std::uint32_t reason) {
if (reason == exit_reason::normal) {
cleanup(exit_reason::normal);
m_behavior_stack.clear();
m_stack.clear();
m_stack.cleanup();
}
else {
abstract_scheduled_actor::quit(reason);
}
}
void event_based_actor::do_become(behavior* bhvr, bool has_ownership) {
reset_timeout();
request_timeout(bhvr->timeout());
if (m_behavior_stack.empty() == false) {
m_erased_stack_elements.emplace_back(std::move(m_behavior_stack.back()));
m_behavior_stack.pop_back();
}
stack_element new_element{bhvr};
if (!has_ownership) new_element.get_deleter().disable();
m_behavior_stack.push_back(std::move(new_element));
void event_based_actor::unbecome() {
m_stack.pop_back();
}
void event_based_actor::on_exit() { }
} // namespace cppa
......@@ -41,7 +41,7 @@
#include "cppa/local_actor.hpp"
#include "cppa/scheduled_actor.hpp"
#include "cppa/thread_mapped_actor.hpp"
#include "cppa/abstract_event_based_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/mock_scheduler.hpp"
......
......@@ -37,13 +37,9 @@ resume_result scheduled_actor_dummy::resume(util::fiber*) {
}
void scheduled_actor_dummy::quit(std::uint32_t) { }
void scheduled_actor_dummy::dequeue(behavior&) { }
void scheduled_actor_dummy::dequeue(partial_function&) { }
void scheduled_actor_dummy::link_to(intrusive_ptr<actor>&) { }
void scheduled_actor_dummy::unlink_from(intrusive_ptr<actor>&) { }
bool scheduled_actor_dummy::establish_backlink(intrusive_ptr<actor>&) {
......@@ -55,9 +51,9 @@ bool scheduled_actor_dummy::remove_backlink(intrusive_ptr<actor>&) {
}
void scheduled_actor_dummy::detach(const attachable::token&) { }
bool scheduled_actor_dummy::attach(attachable*) { return false; }
void scheduled_actor_dummy::unbecome() { }
void scheduled_actor_dummy::do_become(behavior*, bool, bool) { }
bool scheduled_actor_dummy::attach(attachable*) {
return false;
}
} } // namespace cppa::detail
......@@ -108,4 +108,25 @@ detail::filter_result thread_mapped_actor::filter_msg(const any_tuple& msg) {
return detail::ordinary_message;
}
void thread_mapped_actor::unbecome() {
if (m_stack_ptr) {
m_stack_ptr->pop_back();
}
else {
quit();
}
}
void thread_mapped_actor::do_become(behavior* ptr, bool owns_ptr, bool discard){
if (m_stack_ptr) {
if (discard) m_stack_ptr->pop_back();
m_stack_ptr->push_back(ptr, owns_ptr);
}
else {
m_stack_ptr.reset(new detail::behavior_stack);
m_stack_ptr->exec();
quit();
}
}
} // namespace cppa::detail
......@@ -34,7 +34,7 @@
#include <cstddef>
#include <iostream>
#include "cppa/abstract_event_based_actor.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/detail/actor_count.hpp"
#include "cppa/detail/mock_scheduler.hpp"
......
......@@ -14,7 +14,6 @@
#include "cppa/to_string.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/event_based_actor.hpp"
#include "cppa/stacked_event_based_actor.hpp"
using std::cerr;
using std::cout;
......@@ -135,7 +134,7 @@ class event_testee : public fsm_actor<event_testee> {
#endif
// quits after 5 timeouts
abstract_event_based_actor* event_testee2() {
event_based_actor* event_testee2() {
struct impl : fsm_actor<impl> {
behavior wait4timeout(int remaining) {
return (
......
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