// acquires only one lock void append(pointer value) { CAF_REQUIRE(value != nullptr); node* tmp = new node(value); lock_guard guard(m_tail_lock); // publish & swing last forward m_tail.load()->next = tmp; m_tail = tmp; }
bool match_element(size_t pos, uint16_t typenr, const std::type_info* rtti) const override { CAF_REQUIRE(pos < size()); auto& et = m_types[pos]; if (et.first != typenr) { return false; } return et.first != 0 || et.second == rtti || *et.second == *rtti; }
bool match_element(size_t pos, uint16_t typenr, const std::type_info* rtti) const override { CAF_REQUIRE(typenr != 0 || rtti != nullptr); auto uti = m_elements[pos]->ti; if (uti->type_nr() != typenr) { return false; } return typenr != 0 || uti->equal_to(*rtti); }
// acquires both locks, returns nullptr on failure pointer take_tail() { pointer result = nullptr; unique_node_ptr last; { // lifetime scope of guards lock_guard guard1(m_head_lock); lock_guard guard2(m_tail_lock); CAF_REQUIRE(m_head != nullptr); last.reset(m_tail.load()); if (last.get() == m_head.load()) { last.release(); return nullptr; } result = last->value; m_tail = find_predecessor(last.get()); CAF_REQUIRE(m_tail != nullptr); m_tail.load()->next = nullptr; } return result; }
// acquires both locks void prepend(pointer value) { CAF_REQUIRE(value != nullptr); node* tmp = new node(value); node* first = nullptr; // acquire both locks since we might touch m_last too lock_guard guard1(m_head_lock); lock_guard guard2(m_tail_lock); first = m_head.load(); CAF_REQUIRE(first != nullptr); auto next = first->next.load(); // m_first always points to a dummy with no value, // hence we put the new element second if (next == nullptr) { // queue is empty CAF_REQUIRE(first == m_tail); m_tail = tmp; } else { CAF_REQUIRE(first != m_tail); tmp->next = next; } first->next = tmp; }
msg_type filter_msg(Actor* self, mailbox_element* node) { const message& msg = node->msg; auto mid = node->mid; if (msg.size() == 1) { if (msg.type_at(0)->equal_to(typeid(exit_msg))) { auto& em = msg.get_as<exit_msg>(0); CAF_REQUIRE(!mid.valid()); // make sure to get rid of attachables if they're no longer needed self->unlink_from(em.source); if (self->trap_exit() == false) { if (em.reason != exit_reason::normal) { self->quit(em.reason); return msg_type::non_normal_exit; } return msg_type::normal_exit; } } else if (msg.type_at(0)->equal_to(typeid(timeout_msg))) { auto& tm = msg.get_as<timeout_msg>(0); auto tid = tm.timeout_id; CAF_REQUIRE(!mid.valid()); if (self->is_active_timeout(tid)) { return msg_type::timeout; } return self->waits_for_timeout(tid) ? msg_type::inactive_timeout : msg_type::expired_timeout; } else if (msg.type_at(0)->equal_to(typeid(sync_timeout_msg)) && mid.is_response()) { return msg_type::timeout_response; } } if (mid.is_response()) { return self->awaits(mid) ? msg_type::sync_response : msg_type::expired_sync_response; } return msg_type::ordinary; }
bool abstract_actor::unlink_from_impl(const actor_addr& other) { if (!other) { return false; } guard_type guard{m_mtx}; // remove_backlink returns true if this actor is linked to other auto ptr = actor_cast<abstract_actor_ptr>(other); if (!exited() && ptr->remove_backlink(address())) { auto i = std::find(m_links.begin(), m_links.end(), ptr); CAF_REQUIRE(i != m_links.end()); m_links.erase(i); return true; } return false; }
void replace_all(std::string& str, const char (&what)[WhatSize], const char (&with)[WithSize]) { // end(what) - 1 points to the null-terminator auto next = [&](std::string::iterator pos) -> std::string::iterator { return std::search(pos, str.end(), std::begin(what), std::end(what) - 1); }; auto i = next(std::begin(str)); while (i != std::end(str)) { auto before = std::distance(std::begin(str), i); CAF_REQUIRE(before >= 0); str.replace(i, i + WhatSize - 1, with); // i became invalidated -> use new iterator pointing // to the first character after the replaced text i = next(str.begin() + before + (WithSize - 1)); } }
void actor_namespace::write(serializer* sink, const actor_addr& addr) { CAF_REQUIRE(sink != nullptr); if (!addr) { node_id::host_id_type zero; std::fill(zero.begin(), zero.end(), 0); sink->write_value(static_cast<actor_id>(0)); // actor id sink->write_raw(node_id::host_id_size, zero.data()); // host id sink->write_value(static_cast<uint32_t>(0)); // process id } else { // register locally running actors to be able to deserialize them later if (!addr.is_remote()) { auto reg = detail::singletons::get_actor_registry(); reg->put(addr.id(), actor_cast<abstract_actor_ptr>(addr)); } auto pinf = addr.node(); sink->write_value(addr.id()); // actor id sink->write_raw(node_id::host_id_size, pinf.host_id().data()); // host id sink->write_value(pinf.process_id()); // process id } }
uint64_t get_cache_entry(const std::type_info* type_token, const Tuple& value) { CAF_REQUIRE(type_token != nullptr); if (value.dynamically_typed()) { return m_dummy.second; // all groups enabled } size_t i = find_token_pos(type_token); // if we didn't found a cache entry ... if (i == m_cache_end) { // ... 'create' one (override oldest element in cache if full) advance_(m_cache_end); if (m_cache_end == m_cache_begin) { advance_(m_cache_begin); } m_cache[i].first = type_token; idx_token_type idx_token; m_cache[i].second = calc_bitmask(m_cases, idx_token, *type_token, value); } return m_cache[i].second; }
actor_addr actor_namespace::read(deserializer* source) { CAF_REQUIRE(source != nullptr); node_id::host_id_type hid; auto aid = source->read<uint32_t>(); // actor id source->read_raw(node_id::host_id_size, hid.data()); // host id auto pid = source->read<uint32_t>(); // process id node_id this_node = detail::singletons::get_node_id(); if (aid == 0 && pid == 0) { // 0:0 identifies an invalid actor return invalid_actor_addr; } if (pid == this_node.process_id() && hid == this_node.host_id()) { // identifies this exact process on this host, ergo: local actor auto a = detail::singletons::get_actor_registry()->get(aid); // might be invalid return a ? a->address() : invalid_actor_addr; } // identifies a remote actor; create proxy if needed return get_or_put({pid, hid}, aid)->address(); }
void abstract_actor::cleanup(uint32_t reason) { // log as 'actor' CAF_LOGM_TRACE("caf::actor", CAF_ARG(m_id) << ", " << CAF_ARG(reason) << ", " << CAF_ARG(m_is_proxy)); CAF_REQUIRE(reason != exit_reason::not_exited); // move everyhting out of the critical section before processing it decltype(m_links) mlinks; decltype(m_attachables) mattachables; { // lifetime scope of guard guard_type guard{m_mtx}; if (m_exit_reason != exit_reason::not_exited) { // already exited return; } m_exit_reason = reason; mlinks = std::move(m_links); mattachables = std::move(m_attachables); // make sure lists are empty m_links.clear(); m_attachables.clear(); } CAF_LOG_INFO_IF(!is_remote(), "actor with ID " << m_id << " had " << mlinks.size() << " links and " << mattachables.size() << " attached functors; exit reason = " << reason << ", class = " << detail::demangle(typeid(*this))); // send exit messages auto msg = make_message(exit_msg{address(), reason}); CAF_LOGM_DEBUG("caf::actor", "send EXIT to " << mlinks.size() << " links"); for (auto& aptr : mlinks) { aptr->enqueue(address(), message_id {}.with_high_priority(), msg, m_host); } CAF_LOGM_DEBUG("caf::actor", "run " << mattachables.size() << " attachables"); for (attachable_ptr& ptr : mattachables) { ptr->actor_exited(reason); } }
void run() { CAF_LOG_TRACE("worker with ID " << m_id); // scheduling loop for (;;) { auto job = m_queue_policy.internal_dequeue(this); CAF_REQUIRE(job != nullptr); CAF_LOG_DEBUG("resume actor " << id_of(job)); CAF_PUSH_AID_FROM_PTR(dynamic_cast<abstract_actor*>(job)); switch (job->resume(this)) { case resumable::done: { job->detach_from_scheduler(); break; } case resumable::resume_later: { break; } case resumable::shutdown_execution_unit: { m_queue_policy.clear_internal_queue(this); return; } } m_queue_policy.assert_stealable(this); } }
remote_actor_proxy::remote_actor_proxy(actor_id aid, node_id nid, actor parent) : super(aid, nid), m_parent(parent) { CAF_REQUIRE(parent != invalid_actor); CAF_LOG_INFO(CAF_ARG(aid) << ", " << CAF_TARG(nid, to_string)); }
const uniform_type_info* uniform_typeid_by_nr(uint16_t nr) { CAF_REQUIRE(nr > 0 && nr < detail::type_nrs); return uti_map().by_type_nr(nr); }
const uniform_type_info* type_at(size_t pos) const { CAF_REQUIRE(pos < size()); return m_types[pos]; }
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; }
T& get() { CAF_REQUIRE(valid()); return *m_value; }
inline bool local_actor::awaits(message_id response_id) { CAF_REQUIRE(response_id.is_response()); return std::any_of(m_pending_responses.begin(), m_pending_responses.end(), [=](message_id other) { return response_id == other; }); }
void decorated_tuple::init() { CAF_REQUIRE(m_mapping.empty() || *(std::max_element(m_mapping.begin(), m_mapping.end())) < static_cast<const pointer&>(m_decorated)->size()); }
const uniform_type_info* decorated_tuple::type_at(size_t pos) const { CAF_REQUIRE(pos < size()); return m_decorated->type_at(m_mapping[pos]); }
const void* decorated_tuple::at(size_t pos) const { CAF_REQUIRE(pos < size()); return m_decorated->at(m_mapping[pos]); }
void* decorated_tuple::mutable_at(size_t pos) { CAF_REQUIRE(pos < size()); return m_decorated->mutable_at(m_mapping[pos]); }
const void* at(size_t pos) const { CAF_REQUIRE(pos < size()); return tup_ptr_access<0, sizeof...(Ts)>::get(pos, m_data); }
void* mutable_at(size_t pos) { CAF_REQUIRE(pos < size()); return const_cast<void*>(at(pos)); }
const T* operator->() const { CAF_REQUIRE(valid()); return m_value; }
/** * Enqueues a new job to the worker's queue from an internal * source, i.e., a job that is currently executed by this worker. * @warning Must not be called from other threads. */ void exec_later(job_ptr job) override { CAF_REQUIRE(job != nullptr); CAF_LOG_TRACE("id = " << id() << " actor id " << id_of(job)); m_queue_policy.internal_enqueue(this, job); }
const T& operator*() const { CAF_REQUIRE(valid()); return *m_value; }
response_promise::response_promise(const actor_addr& from, const actor_addr& to, const message_id& id) : m_from(from), m_to(to), m_id(id) { CAF_REQUIRE(id.is_response() || !id.valid()); }
const T& get() const { CAF_REQUIRE(valid()); return *m_value; }