Commit c3beb4d6 authored by Dominik Charousset's avatar Dominik Charousset

revamped spawn to use event-based impl by default

this patch turns libcppa's spawn function family upside down;
actors are now event-based by default, but users can opt out
by using `spawn<blocking_api>(...)`;
this patch also removes the old `scheduling_hint` interface and
replaces it with the more versatile `spawn_options`, e.g.,
`spawn_monitor<detached>(...)` becomes `spawn<monitored + detached>(...)`
parent 55bc8869
...@@ -125,7 +125,6 @@ cppa/response_handle.hpp ...@@ -125,7 +125,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
...@@ -293,3 +292,4 @@ cppa/memory_cached_mixin.hpp ...@@ -293,3 +292,4 @@ cppa/memory_cached_mixin.hpp
unit_testing/test.cpp unit_testing/test.cpp
src/exit_reason.cpp src/exit_reason.cpp
src/on.cpp src/on.cpp
cppa/spawn_options.hpp
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#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"
......
...@@ -55,10 +55,10 @@ ...@@ -55,10 +55,10 @@
#include "cppa/tuple_cast.hpp" #include "cppa/tuple_cast.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"
...@@ -417,11 +417,11 @@ inline void send_impl(T* whom, any_tuple&& what) { ...@@ -417,11 +417,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 +436,7 @@ inline void send_tpl_impl(T* whom, Args&&... what) { ...@@ -436,7 +436,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 +446,12 @@ send_tuple(const intrusive_ptr<C>& whom, any_tuple what) { ...@@ -446,12 +446,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 +460,9 @@ send(const intrusive_ptr<C>& whom, Args&&... what) { ...@@ -460,9 +460,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 +474,12 @@ send_tuple_as(const actor_ptr& from, const intrusive_ptr<C>& whom, any_tuple wha ...@@ -474,12 +474,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 +522,13 @@ inline message_future sync_send_tuple(const actor_ptr& whom, any_tuple what) { ...@@ -522,13 +522,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 +543,7 @@ inline message_future sync_send(const actor_ptr& whom, Args&&... what) { ...@@ -543,7 +543,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 +564,17 @@ message_future timed_sync_send_tuple(const actor_ptr& whom, ...@@ -564,17 +564,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,18 +589,18 @@ inline void reply_tuple(any_tuple what) { ...@@ -589,18 +589,18 @@ 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)...));
} }
} }
...@@ -626,7 +626,7 @@ inline void forward_to(const actor_ptr& whom) { ...@@ -626,7 +626,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 +640,15 @@ inline void delayed_send_tuple(const channel_ptr& whom, ...@@ -640,15 +640,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 +659,7 @@ inline void delayed_send(const channel_ptr& whom, ...@@ -659,7 +659,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,10 +675,10 @@ inline void delayed_reply_tuple(const std::chrono::duration<Rep, Period>& rtime, ...@@ -675,10 +675,10 @@ 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)...));
} }
/** @} */ /** @} */
...@@ -688,14 +688,19 @@ inline void delayed_reply(const std::chrono::duration<Rep, Period>& rtime, ...@@ -688,14 +688,19 @@ inline void delayed_reply(const std::chrono::duration<Rep, Period>& rtime,
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;
} }
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);
}
#endif // CPPA_DOCUMENTATION #endif // CPPA_DOCUMENTATION
/** /**
...@@ -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}.
*/
template<scheduling_hint Hint, typename Fun, typename... Args>
actor_ptr spawn(Fun&& fun, Args&&... args) {
return get_scheduler()->spawn_impl(Hint,
std::forward<Fun>(fun),
std::forward<Args>(args)...);
}
/**
* @brief Spawns a new context-switching {@link actor}
* that executes <tt>fun(args...)</tt>.
* @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(Fun&& fun, Args&&... args) {
return spawn<scheduled>(std::forward<Fun>(fun),
std::forward<Args>(args)...);
}
/**
* @brief Spawns a new context-switching or thread-mapped {@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.
* @tparam Hint A hint to the scheduler for the best scheduling strategy.
* @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<scheduling_hint Hint, typename Fun, typename... Args>
actor_ptr spawn_in_group(const group_ptr& grp, Fun&& fun, Args&&... args) {
return get_scheduler()->spawn_cb_impl(Hint,
[grp](local_actor* ptr) {
ptr->join(grp);
},
std::forward<Fun>(fun),
std::forward<Args>(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}. * @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> template<spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn_in_group(Fun&& fun, Args&&... args) { actor_ptr spawn(Ts&&... args) {
return spawn_in_group<scheduled>(std::forward<Fun>(fun), static_assert(sizeof...(Ts) > 0, "too few arguments provided");
std::forward<Args>(args)...); return eval_sopts(Options,
get_scheduler()->exec(Options,
scheduler::init_callback{},
std::forward<Ts>(args)...));
} }
/** /**
* @brief Spawns an actor of type @p ActorImpl. * @brief Spawns an actor of type @p Impl.
* @param args Optional constructor arguments. * @param args Constructor arguments.
* @tparam ActorImpl Subtype of {@link event_based_actor} or {@link sb_actor}. * @tparam Impl Subtype of {@link event_based_actor} or {@link sb_actor}.
* @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}.
*/ */
template<class ActorImpl, typename... Args> template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn(Args&&... args) { actor_ptr spawn(Ts&&... args) {
auto ptr = detail::memory::create<ActorImpl>(std::forward<Args>(args)...); auto rawptr = detail::memory::create<Impl>(std::forward<Ts>(args)...);
return get_scheduler()->spawn(ptr); return eval_sopts(Options, get_scheduler()->exec(Options, rawptr));
} }
/** /**
* @brief Spawns an actor of type @p ActorImpl that joins @p grp immediately. * @brief Spawns a new actor that evaluates given arguments and
* @param grp The group that the newly created actor shall join. * immediately joins @p grp.
* @param args Optional constructor arguments. * @param args A functor followed by its arguments.
* @tparam ActorImpl Subtype of {@link event_based_actor} or {@link sb_actor}. * @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 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<class ActorImpl, typename... Args> template<spawn_options Options = no_spawn_options, typename... Ts>
actor_ptr spawn_in_group(const group_ptr& grp, Args&&... args) { actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) {
auto ptr = detail::memory::create<ActorImpl>(std::forward<Args>(args)...); static_assert(sizeof...(Ts) > 0, "too few arguments provided");
return get_scheduler()->spawn(ptr, [&](local_actor* p) { p->join(grp); }); auto init_cb = [=](local_actor* ptr) {
} ptr->join(grp);
};
#ifndef CPPA_DOCUMENTATION return eval_sopts(Options,
get_scheduler()->exec(Options,
template<class ActorImpl, typename... Args> init_cb,
actor_ptr spawn_hidden_in_group(const group_ptr& grp, Args&&... args) { std::forward<Ts>(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} * @brief Spawns an actor of type @p Impl that immediately joins @p grp.
* that executes <tt>fun(args...)</tt> and creates a link between the * @param args Constructor arguments.
* calling actor and the 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_link(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->link_to(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 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}
* 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.
* @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_monitor(Fun&& fun, Args&&... args) {
auto res = spawn<Hint>(std::forward<Fun>(fun), std::forward<Args>(args)...);
self->monitor(res);
return res;
}
/**
* @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;
} }
/** @} */ /** @} */
...@@ -988,6 +846,13 @@ inline void quit_actor(const actor_ptr& whom, std::uint32_t reason) { ...@@ -988,6 +846,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&);
......
...@@ -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:
......
...@@ -47,31 +47,24 @@ class thread_pool_scheduler : public scheduler { ...@@ -47,31 +47,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 +79,6 @@ class thread_pool_scheduler : public scheduler { ...@@ -86,10 +79,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
......
...@@ -87,6 +87,8 @@ class event_based_actor : public detail::abstract_scheduled_actor { ...@@ -87,6 +87,8 @@ class event_based_actor : public detail::abstract_scheduled_actor {
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();
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#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/local_actor.hpp" #include "cppa/local_actor.hpp"
...@@ -46,7 +48,8 @@ enum class resume_result { ...@@ -46,7 +48,8 @@ enum class resume_result {
enum scheduled_actor_type { enum scheduled_actor_type {
context_switching_impl, context_switching_impl,
event_based_impl event_based_impl,
default_event_based_impl // scheduler enqueues a 'RUN' message on startup
}; };
/** /**
......
...@@ -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
/** /**
...@@ -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,72 +161,26 @@ class scheduler { ...@@ -157,72 +161,26 @@ 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;
/**
* @brief Spawns a new actor that executes <code>behavior()</code>
* with the scheduling policy @p hint if possible and calls
* <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, scheduled_actor_ptr ptr) = 0;
init_callback init_cb,
scheduling_hint hint) = 0;
/** /**
* @brief Spawns a new event-based actor. * @brief Creates a new actor from @p actor_behavior and executes it
* in this scheduler.
*/ */
virtual actor_ptr spawn(scheduled_actor* what, virtual actor_ptr exec(spawn_options opts,
scheduling_hint hint = scheduled) = 0; init_callback init_cb,
void_function actor_behavior) = 0;
/**
* @brief Spawns a new event-based actor and calls
* <code>init_cb</code> after the actor is initialized but before
* it starts execution.
*/
virtual actor_ptr spawn(scheduled_actor* what,
init_callback init_cb,
scheduling_hint hint = scheduled) = 0;
// hide implementation details for documentation // hide implementation details for documentation
# ifndef CPPA_DOCUMENTATION # ifndef CPPA_DOCUMENTATION
template<typename Fun, typename Arg0, typename... Args> template<typename F, typename T0, typename... Ts>
actor_ptr spawn_impl(scheduling_hint hint, Fun&& fun, Arg0&& arg0, Args&&... args) { actor_ptr exec(spawn_options opts, init_callback cb,
return this->spawn( F f, T0&& a0, Ts&&... as) {
std::bind( return this->exec(opts, cb, std::bind(f, detail::fwd<T0>(a0),
std::forward<Fun>(fun), detail::fwd<Ts>(as)...));
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 # endif // CPPA_DOCUMENTATION
......
...@@ -28,41 +28,130 @@ ...@@ -28,41 +28,130 @@
\******************************************************************************/ \******************************************************************************/
#ifndef CPPA_SCHEDULING_HINT_HPP #ifndef CPPA_SPAWN_OPTIONS_HPP
#define CPPA_SCHEDULING_HINT_HPP #define CPPA_SPAWN_OPTIONS_HPP
namespace cppa { namespace cppa {
/** /**
* @brief Denotes whether a user wants an actor to take part in * @ingroup ActorCreation
* cooperative scheduling or not. * @{
*/ */
enum scheduling_hint {
/**
* @brief Indicates that an actor takes part in cooperative scheduling.
*/
scheduled,
/**
* @brief Indicates that an actor should run in its own thread.
*/
detached,
/**
* @brief Indicates that an actor should run in its own thread,
* but it is ignored by {@link await_others_done()}.
*/
detached_and_hidden,
/**
* @brief Indicates that an actor takes part in cooperative scheduling,
* but it is ignored by {@link await_others_done()}.
*/
scheduled_and_hidden
/**
* @brief Stores options passed to the @p spawn function family.
*/
enum class spawn_options : int {
no_flags = 0x00,
link_flag = 0x01,
monitor_flag = 0x02,
detach_flag = 0x04,
hide_flag = 0x08,
blocking_api_flag = 0x10
}; };
#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 } // namespace cppa
#endif // CPPA_SCHEDULING_HINT_HPP #endif // CPPA_SPAWN_OPTIONS_HPP
...@@ -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,40 +23,35 @@ void math_fun() { ...@@ -23,40 +23,35 @@ 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) { reply(atom("result"), a + b);
reply(atom("result"), a + b); },
}, on(atom("minus"), arg_match) >> [](int a, int b) {
on(atom("minus"), arg_match) >> [](int a, int b) { reply(atom("result"), a - b);
reply(atom("result"), a - b); },
}, // 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")) >> [=]() { // terminate actor with normal exit reason
// set an empty behavior self->quit();
// (terminates actor with normal exit reason) }
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));
......
...@@ -31,11 +31,43 @@ ...@@ -31,11 +31,43 @@
#include <iostream> #include <iostream>
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/on.hpp"
#include "cppa/self.hpp" #include "cppa/self.hpp"
#include "cppa/event_based_actor.hpp" #include "cppa/event_based_actor.hpp"
namespace cppa { namespace cppa {
class default_scheduled_actor : public event_based_actor {
public:
typedef std::function<void()> fun_type;
default_scheduled_actor(fun_type&& fun) : m_fun(std::move(fun)) { }
void init() {
become (
on(atom("RUN")) >> [=] {
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(super::blocked) { } event_based_actor::event_based_actor() : super(super::blocked) { }
void event_based_actor::dequeue(behavior&) { void event_based_actor::dequeue(behavior&) {
......
...@@ -108,7 +108,7 @@ struct group_nameserver : event_based_actor { ...@@ -108,7 +108,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);
} }
......
...@@ -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) {
...@@ -426,7 +426,7 @@ class remote_group_module : public group::module { ...@@ -426,7 +426,7 @@ 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<hidden>([_this, sm] {
typedef map<string, pair<actor_ptr, vector<pair<string, remote_group_ptr>>>> typedef map<string, pair<actor_ptr, vector<pair<string, remote_group_ptr>>>>
peer_map; peer_map;
peer_map peers; peer_map peers;
...@@ -529,7 +529,7 @@ local_group::local_group(bool spawn_local_broker, ...@@ -529,7 +529,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) {
......
...@@ -28,8 +28,12 @@ ...@@ -28,8 +28,12 @@
\******************************************************************************/ \******************************************************************************/
#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/memory.hpp"
namespace cppa { namespace cppa {
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#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_count.hpp"
#include "cppa/detail/singleton_manager.hpp" #include "cppa/detail/singleton_manager.hpp"
...@@ -108,7 +109,7 @@ class delayed_msg { ...@@ -108,7 +109,7 @@ class delayed_msg {
private: private:
either<async_send_fun, sync_reply_fun> fun; either<async_send_fun,sync_reply_fun> fun;
channel_ptr ptr_a; channel_ptr ptr_a;
actor_ptr ptr_b; actor_ptr ptr_b;
...@@ -347,5 +348,4 @@ const actor_ptr& scheduler::printer() const { ...@@ -347,5 +348,4 @@ const actor_ptr& scheduler::printer() const {
return m_helper->m_printer; return m_helper->m_printer;
} }
} // namespace cppa } // namespace cppa
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <cstddef> #include <cstddef>
#include <iostream> #include <iostream>
#include "cppa/on.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"
...@@ -46,13 +47,6 @@ using std::endl; ...@@ -46,13 +47,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,38 +65,28 @@ struct thread_pool_scheduler::worker { ...@@ -71,38 +65,28 @@ 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));
} }
} }
...@@ -110,37 +94,25 @@ struct thread_pool_scheduler::worker { ...@@ -110,37 +94,25 @@ struct thread_pool_scheduler::worker {
void operator()() { void operator()() {
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) {
job = less_aggressive_polling();
if (job == nullptr) {
job = relaxed_polling();
}
}
if (job == m_dummy) { if (job == m_dummy) {
// 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.reset();
next_job.reset(); if (job->resume(&fself, next) == resume_result::actor_done) {
if (job->resume(&fself, next_job) == resume_result::actor_done) { bool hidden = job->is_hidden();
bool hidden = job->is_hidden(); job->deref();
job->deref(); if (!hidden) dec_actor_count();
//std::atomic_thread_fence(std::memory_order_seq_cst);
if (!hidden) dec_actor_count();
}
if (next_job) {
job = static_cast<job_ptr>(next_job.get());
//get_scheduler()->printer()->enqueue(job, make_any_tuple("fast-forwarded execution (chained actor)\n"));
}
else job = nullptr;
} }
while (job); // loops until next_job was nullptr if (next) job = static_cast<job_ptr>(next.get());
else job = nullptr;
} }
while (job); // loops until next == nullptr
} }
} }
...@@ -151,14 +123,13 @@ void thread_pool_scheduler::worker_loop(thread_pool_scheduler::worker* w) { ...@@ -151,14 +123,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) {
...@@ -201,104 +172,71 @@ void thread_pool_scheduler::enqueue(scheduled_actor* what) { ...@@ -201,104 +172,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) { inc_actor_count(); }
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 (...) { }
std::atomic_thread_fence(std::memory_order_seq_cst); if (!is_hidden) {
if (!hidden) dec_actor_count(); std::atomic_thread_fence(std::memory_order_seq_cst);
}).detach(); dec_actor_count();
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,
scheduling_hint hint) {
scheduled_actor_ptr ptr{raw};
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden);
return spawn_impl(std::move(ptr));
}
actor_ptr thread_pool_scheduler::spawn(scheduled_actor* raw,
init_callback cb,
scheduling_hint hint) {
scheduled_actor_ptr ptr{raw};
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden);
cb(ptr.get());
return spawn_impl(std::move(ptr));
} }
#ifndef CPPA_DISABLE_CONTEXT_SWITCHING actor_ptr thread_pool_scheduler::exec(spawn_options os, scheduled_actor_ptr p) {
CPPA_REQUIRE(p != nullptr);
actor_ptr thread_pool_scheduler::spawn(void_function fun, scheduling_hint hint) { bool is_hidden = has_hide_flag(os);
if (hint == scheduled || hint == scheduled_and_hidden) { if (has_detach_flag(os)) {
scheduled_actor_ptr ptr{detail::memory::create<context_switching_actor>(std::move(fun))}; exec_as_thread(is_hidden, p, [p] {
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden); p->exec_behavior_stack();
return spawn_impl(std::move(ptr)); p->on_exit();
});
return std::move(p);
} }
else { p->attach_to_scheduler(this, is_hidden);
return spawn_as_thread(std::move(fun), if (p->has_behavior()) {
[](local_actor*) { }, if (!is_hidden) { inc_actor_count(); }
hint == detached_and_hidden); 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(void_function fun, actor_ptr thread_pool_scheduler::exec(spawn_options os,
init_callback init_cb, init_callback cb,
scheduling_hint hint) { void_function f) {
if (hint == scheduled || hint == scheduled_and_hidden) { if (has_blocking_api_flag(os)) {
scheduled_actor_ptr ptr{detail::memory::create<context_switching_actor>(std::move(fun))}; # ifndef CPPA_DISABLE_CONTEXT_SWITCHING
ptr->attach_to_scheduler(this, hint == scheduled_and_hidden); if (!has_detach_flag(os)) {
init_cb(ptr.get()); return exec(os,
return spawn_impl(std::move(ptr)); memory::create<context_switching_actor>(std::move(f)));
} }
else { # endif
return spawn_as_thread(std::move(fun), thread_mapped_actor_ptr p;
std::move(init_cb), p.reset(memory::create<thread_mapped_actor>(std::move(f)));
hint == detached_and_hidden); exec_as_thread(has_hide_flag(os), p, [p] {
p->run();
p->on_exit();
});
return std::move(p);
} }
auto p = event_based_actor::from(std::move(f));
if (cb) cb(p.get());
return exec(os, p);
} }
#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
...@@ -157,7 +157,7 @@ int client_part(const vector<string_pair>& args) { ...@@ -157,7 +157,7 @@ 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();
......
...@@ -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);
} }
); );
} }
...@@ -423,7 +410,7 @@ int main() { ...@@ -423,7 +410,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 +616,23 @@ int main() { ...@@ -629,27 +616,23 @@ 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 20,000 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 twenty_thousand = spawn([] {
for (int i = 0; i < 20000; ++i) { for (int i = 0; i < 20000; ++i) {
spawn_link<event_testee>(); spawn<event_testee, linked>();
} }
receive_loop ( become(others() >> CPPA_UNEXPECTED_MSG_CB());
others() >> []() {
cout << "wtf? => " << to_string(self->last_dequeued()) << endl;
}
);
}); });
quit_actor(twenty_thousand, exit_reason::user_defined); quit_actor(twenty_thousand, exit_reason::user_defined);
await_all_others_done(); await_all_others_done();
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;
......
...@@ -171,9 +171,9 @@ int main() { ...@@ -171,9 +171,9 @@ 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>([&] {
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>([] { // 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