void run_detached() { auto dthis = util::dptr<Subtype>(this); dthis->init(); if (dthis->planned_exit_reason() != exit_reason::not_exited) { // init() did indeed call quit() for some reason dthis->on_exit(); } while (!dthis->m_bhvr_stack.empty()) { dthis->m_bhvr_stack.exec(dthis->m_recv_policy, dthis); dthis->on_exit(); } auto rsn = dthis->planned_exit_reason(); dthis->cleanup(rsn == exit_reason::not_exited ? exit_reason::normal : rsn); }
void local_actor::quit(uint32_t reason) { CAF_LOG_TRACE("reason = " << reason << ", class " << detail::demangle(typeid(*this))); planned_exit_reason(reason); if (is_blocking()) { throw actor_exited(reason); } }
void abstract_broker::cleanup(uint32_t reason) { CAF_LOG_TRACE(CAF_ARG(reason)); planned_exit_reason(reason); on_exit(); close_all(); CAF_ASSERT(doormen_.empty()); CAF_ASSERT(scribes_.empty()); CAF_ASSERT(current_mailbox_element() == nullptr); cache_.clear(); local_actor::cleanup(reason); deref(); // release implicit reference count from middleman }
void context_switching_resume::trampoline(void* this_ptr) { CPPA_LOGF_TRACE(CPPA_ARG(this_ptr)); auto self = reinterpret_cast<blocking_actor*>(this_ptr); auto shut_actor_down = [self](std::uint32_t reason) { if (self->planned_exit_reason() == exit_reason::not_exited) { self->planned_exit_reason(reason); } self->on_exit(); self->cleanup(self->planned_exit_reason()); }; try { self->act(); shut_actor_down(exit_reason::normal); } catch (actor_exited& e) { shut_actor_down(e.reason()); } catch (...) { shut_actor_down(exit_reason::unhandled_exception); } std::atomic_thread_fence(std::memory_order_seq_cst); CPPA_LOGF_DEBUG("done, yield() back to execution unit");; detail::yield(detail::yield_state::done); }
void local_actor::quit(std::uint32_t reason) { CPPA_LOG_TRACE("reason = " << reason << ", class " << detail::demangle(typeid(*this))); if (reason == exit_reason::unallowed_function_call) { // this is the only reason that causes an exception cleanup(reason); CPPA_LOG_WARNING("actor tried to use a blocking function"); // when using receive(), the non-blocking nature of event-based // actors breaks any assumption the user has about his code, // in particular, receive_loop() is a deadlock when not throwing // an exception here aout(this) << "*** warning: event-based actor killed because it tried " "to use receive()\n"; throw actor_exited(reason); } planned_exit_reason(reason); }
resumable::resume_result resume(detail::cs_thread*, execution_unit* host) override { auto d = static_cast<Derived*>(this); d->m_host = host; CPPA_LOG_TRACE("id = " << d->id()); auto done_cb = [&]() -> bool { CPPA_LOG_TRACE(""); d->bhvr_stack().clear(); d->bhvr_stack().cleanup(); d->on_exit(); if (!d->bhvr_stack().empty()) { CPPA_LOG_DEBUG("on_exit did set a new behavior in on_exit"); d->planned_exit_reason(exit_reason::not_exited); return false; // on_exit did set a new behavior } auto rsn = d->planned_exit_reason(); if (rsn == exit_reason::not_exited) { rsn = exit_reason::normal; d->planned_exit_reason(rsn); } d->cleanup(rsn); return true; }; auto actor_done = [&] { return d->bhvr_stack().empty() || d->planned_exit_reason() != exit_reason::not_exited; }; // actors without behavior or that have already defined // an exit reason must not be resumed CPPA_REQUIRE(!d->m_initialized || !actor_done()); if (!d->m_initialized) { d->m_initialized = true; auto bhvr = d->make_behavior(); if (bhvr) d->become(std::move(bhvr)); // else: make_behavior() might have just called become() if (actor_done() && done_cb()) return resume_result::done; // else: enter resume loop } try { for (;;) { auto ptr = d->next_message(); if (ptr) { if (d->invoke_message(ptr)) { if (actor_done() && done_cb()) { CPPA_LOG_DEBUG("actor exited"); return resume_result::done; } // continue from cache if current message was // handled, because the actor might have changed // its behavior to match 'old' messages now while (d->invoke_message_from_cache()) { if (actor_done() && done_cb()) { CPPA_LOG_DEBUG("actor exited"); return resume_result::done; } } } // add ptr to cache if invoke_message // did not reset it (i.e. skipped, but not dropped) if (ptr) { CPPA_LOG_DEBUG("add message to cache"); d->push_to_cache(std::move(ptr)); } } else { CPPA_LOG_DEBUG("no more element in mailbox; " "going to block"); if (d->mailbox().try_block()) { return resumable::resume_later; } // else: try again } } } catch (actor_exited& what) { CPPA_LOG_INFO("actor died because of exception: actor_exited, " "reason = " << what.reason()); if (d->exit_reason() == exit_reason::not_exited) { d->quit(what.reason()); } } catch (std::exception& e) { CPPA_LOG_WARNING("actor died because of exception: " << detail::demangle(typeid(e)) << ", what() = " << e.what()); if (d->exit_reason() == exit_reason::not_exited) { d->quit(exit_reason::unhandled_exception); } } catch (...) { CPPA_LOG_WARNING("actor died because of an unknown exception"); if (d->exit_reason() == exit_reason::not_exited) { d->quit(exit_reason::unhandled_exception); } } done_cb(); return resumable::done; }
resumable::resume_result resume(execution_unit* new_host, size_t max_throughput) override { CAF_REQUIRE(max_throughput > 0); auto d = static_cast<Derived*>(this); CAF_LOG_TRACE("id = " << d->id()); d->host(new_host); auto done_cb = [&]() -> bool { CAF_LOG_TRACE(""); d->bhvr_stack().clear(); d->bhvr_stack().cleanup(); d->on_exit(); if (!d->bhvr_stack().empty()) { CAF_LOG_DEBUG("on_exit did set a new behavior"); d->planned_exit_reason(exit_reason::not_exited); return false; // on_exit did set a new behavior } auto rsn = d->planned_exit_reason(); if (rsn == exit_reason::not_exited) { rsn = exit_reason::normal; d->planned_exit_reason(rsn); } d->cleanup(rsn); return true; }; auto actor_done = [&]() -> bool { if (d->bhvr_stack().empty() || d->planned_exit_reason() != exit_reason::not_exited) { return done_cb(); } return false; }; // actors without behavior or that have already defined // an exit reason must not be resumed CAF_REQUIRE(!d->is_initialized() || (!d->bhvr_stack().empty() && d->planned_exit_reason() == exit_reason::not_exited)); std::exception_ptr eptr = nullptr; try { if (!d->is_initialized()) { CAF_LOG_DEBUG("initialize actor"); d->is_initialized(true); auto bhvr = d->make_behavior(); CAF_LOG_DEBUG_IF(!bhvr, "make_behavior() did not return a behavior, " << "bhvr_stack().empty() = " << std::boolalpha << d->bhvr_stack().empty()); if (bhvr) { // make_behavior() did return a behavior instead of using become() CAF_LOG_DEBUG("make_behavior() did return a valid behavior"); d->become(std::move(bhvr)); } if (actor_done()) { CAF_LOG_DEBUG("actor_done() returned true right " << "after make_behavior()"); return resume_result::done; } } // max_throughput = 0 means infinite for (size_t i = 0; i < max_throughput; ++i) { auto ptr = d->next_message(); if (ptr) { if (d->invoke_message(ptr)) { if (actor_done()) { CAF_LOG_DEBUG("actor exited"); return resume_result::done; } // continue from cache if current message was // handled, because the actor might have changed // its behavior to match 'old' messages now while (d->invoke_message_from_cache()) { if (actor_done()) { CAF_LOG_DEBUG("actor exited"); return resume_result::done; } } } // add ptr to cache if invoke_message // did not reset it (i.e. skipped, but not dropped) if (ptr) { CAF_LOG_DEBUG("add message to cache"); d->push_to_cache(std::move(ptr)); } } else { CAF_LOG_DEBUG("no more element in mailbox; going to block"); if (d->mailbox().try_block()) { return resumable::awaiting_message; } CAF_LOG_DEBUG("try_block() interrupted by new message"); } } if (!d->has_next_message() && d->mailbox().try_block()) { return resumable::awaiting_message; } // time's up return resumable::resume_later; } catch (actor_exited& what) { CAF_LOG_INFO("actor died because of exception: actor_exited, " "reason = " << what.reason()); if (d->exit_reason() == exit_reason::not_exited) { d->quit(what.reason()); } } catch (std::exception& e) { CAF_LOG_INFO("actor died because of an exception: " << detail::demangle(typeid(e)) << ", what() = " << e.what()); if (d->exit_reason() == exit_reason::not_exited) { d->quit(exit_reason::unhandled_exception); } eptr = std::current_exception(); } catch (...) { CAF_LOG_INFO("actor died because of an unknown exception"); if (d->exit_reason() == exit_reason::not_exited) { d->quit(exit_reason::unhandled_exception); } eptr = std::current_exception(); } if (eptr) { auto opt_reason = d->handle(eptr); if (opt_reason) { // use exit reason defined by custom handler d->planned_exit_reason(*opt_reason); } } if (!actor_done()) { // actor has been "revived", try running it again later return resumable::resume_later; } return resumable::done; }
void blocking_actor::quit(std::uint32_t reason) { planned_exit_reason(reason); throw actor_exited(reason); }