Commit 7e1cf972 authored by Dominik Charousset's avatar Dominik Charousset

Use intrusive singly-linked attachables

By replacing the vector in `local_actor` with singly-linked attachables, we
save 16 Bytes on 64 bit machines. Relates #180.
parent d2ca6275
......@@ -263,6 +263,16 @@ class abstract_actor : public abstract_channel {
m_host = new_host;
}
inline void attach_impl(attachable_ptr& ptr) {
ptr->next.swap(m_attachables_head);
m_attachables_head.swap(ptr);
}
static size_t detach_impl(const attachable::token& what,
attachable_ptr& ptr,
bool stop_on_first_hit = false,
bool dry_run = false);
// cannot be changed after construction
const actor_id m_id;
......@@ -273,7 +283,7 @@ class abstract_actor : public abstract_channel {
mutable std::mutex m_mtx;
// attached functors that are executed on cleanup (for monitors, links, etc)
std::vector<attachable_ptr> m_attachables;
attachable_ptr m_attachables_head;
// identifies the execution unit this actor is currently executed by
execution_unit* m_host;
......
......@@ -41,6 +41,11 @@ class attachable {
* Represents a pointer to a value with its RTTI.
*/
struct token {
template <class T>
token(const T& tk) : subtype(typeid(T)), ptr(&tk) {
// nop
}
/**
* Denotes the type of ptr.
*/
......@@ -74,6 +79,8 @@ class attachable {
bool matches(const T& what) {
return matches(token{typeid(T), &what});
}
std::unique_ptr<attachable> next;
};
/**
......
......@@ -23,6 +23,7 @@
#include <atomic>
#include <cstdint>
#include <functional>
#include <forward_list>
#include "caf/actor.hpp"
#include "caf/extend.hpp"
......@@ -534,9 +535,6 @@ class local_actor : public extend<abstract_actor>::with<mixin::memory_cached> {
// points to the node under processing otherwise
mailbox_element* m_current_node;
// {group => subscription} map of all joined groups
// std::map<group, abstract_group::subscription> m_subscriptions;
// set by quit
uint32_t m_planned_exit_reason;
......
......@@ -75,8 +75,7 @@ void abstract_actor::attach(attachable_ptr ptr) {
guard_type guard{m_mtx};
reason = m_exit_reason;
if (reason == exit_reason::not_exited) {
m_attachables.push_back(std::move(ptr));
CAF_LOG_DEBUG("actor has now " << m_attachables.size() << " attachables");
attach_impl(ptr);
return;
}
}
......@@ -84,19 +83,27 @@ void abstract_actor::attach(attachable_ptr ptr) {
ptr->actor_exited(this, reason);
}
void abstract_actor::detach(const attachable::token& what) {
attachable_ptr ptr;
{ // lifetime scope of guard
guard_type guard{m_mtx};
auto end = m_attachables.end();
auto i = std::find_if(m_attachables.begin(), end,
[&](attachable_ptr& p) { return p->matches(what); });
if (i != end) {
ptr.swap(*i);
m_attachables.erase(i);
size_t abstract_actor::detach_impl(const attachable::token& what,
attachable_ptr& ptr,
bool stop_on_hit,
bool dry_run) {
if (!ptr) {
return 0;
}
if (ptr->matches(what)) {
if (!dry_run) {
attachable_ptr next;
next.swap(ptr->next);
ptr.swap(next);
}
// ptr will be destroyed here, without locked mutex
return stop_on_hit ? 1 : 1 + detach_impl(what, ptr, stop_on_hit, dry_run);
}
return detach_impl(what, ptr->next, stop_on_hit, dry_run);
}
void abstract_actor::detach(const attachable::token& what) {
guard_type guard{m_mtx};
detach_impl(what, m_attachables_head);
}
bool abstract_actor::link_impl(linking_operation op, const actor_addr& other) {
......@@ -121,11 +128,11 @@ bool abstract_actor::establish_link_impl(const actor_addr& other) {
if (exited()) {
ptr->enqueue(address(), message_id::invalid,
make_message(exit_msg{address(), exit_reason()}), m_host);
}
} else if (ptr->establish_backlink(address())) {
// add link if not already linked to other
// (checked by establish_backlink)
else if (ptr->establish_backlink(address())) {
m_attachables.push_back(default_attachable::make_link(other));
auto tmp = default_attachable::make_link(other);
attach_impl(tmp);
return true;
}
}
......@@ -134,14 +141,14 @@ bool abstract_actor::establish_link_impl(const actor_addr& other) {
bool abstract_actor::establish_backlink_impl(const actor_addr& other) {
uint32_t reason = exit_reason::not_exited;
default_attachable::predicate pred(other, default_attachable::link);
default_attachable::observe_token tk{other, default_attachable::link};
if (other && other != this) {
guard_type guard{m_mtx};
reason = m_exit_reason;
if (reason == exit_reason::not_exited) {
auto i = std::find_if(m_attachables.begin(), m_attachables.end(), pred);
if (i == m_attachables.end()) {
m_attachables.push_back(default_attachable::make_link(other));
if (detach_impl(tk, m_attachables_head, true, true) == 0) {
auto tmp = default_attachable::make_link(other);
attach_impl(tmp);
return true;
}
}
......@@ -159,28 +166,22 @@ bool abstract_actor::remove_link_impl(const actor_addr& other) {
if (!other) {
return false;
}
default_attachable::predicate pred(other, default_attachable::link);
default_attachable::observe_token tk{other, default_attachable::link};
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_if(m_attachables.begin(), m_attachables.end(), pred);
CAF_REQUIRE(i != m_attachables.end());
m_attachables.erase(i);
detach_impl(tk, m_attachables_head, true);
return true;
}
return false;
}
bool abstract_actor::remove_backlink_impl(const actor_addr& other) {
default_attachable::predicate pred(other, default_attachable::link);
default_attachable::observe_token tk{other, default_attachable::link};
if (other && other != this) {
guard_type guard{m_mtx};
auto i = std::find_if(m_attachables.begin(), m_attachables.end(), pred);
if (i != m_attachables.end()) {
m_attachables.erase(i);
return true;
}
return detach_impl(tk, m_attachables_head, true) > 0;
}
return false;
}
......@@ -194,7 +195,7 @@ void abstract_actor::cleanup(uint32_t reason) {
CAF_LOGM_TRACE("caf::actor", CAF_ARG(m_id) << ", " << CAF_ARG(reason));
CAF_REQUIRE(reason != exit_reason::not_exited);
// move everyhting out of the critical section before processing it
decltype(m_attachables) mattachables;
attachable_ptr head;
{ // lifetime scope of guard
guard_type guard{m_mtx};
if (m_exit_reason != exit_reason::not_exited) {
......@@ -202,17 +203,15 @@ void abstract_actor::cleanup(uint32_t reason) {
return;
}
m_exit_reason = reason;
mattachables.swap(m_attachables);
m_attachables_head.swap(head);
}
CAF_LOG_INFO_IF(!is_remote(), "actor with ID "
<< m_id << " had " << mattachables.size()
<< " attachables; exit reason = "
CAF_LOG_INFO_IF(!is_remote(), "cleanup actor with ID "
<< m_id << "; exit reason = "
<< reason << ", class = "
<< detail::demangle(typeid(*this)));
// send exit messages
CAF_LOGM_DEBUG("caf::actor", "run " << mattachables.size() << " attachables");
for (attachable_ptr& ptr : mattachables) {
ptr->actor_exited(this, reason);
for (attachable* i = head.get(); i != nullptr; i = i->next.get()) {
i->actor_exited(this, reason);
}
}
......
......@@ -66,14 +66,12 @@ void local_actor::join(const group& what) {
if (what == invalid_group) {
return;
}
abstract_group::subscription_predicate pred{what.ptr()};
abstract_group::subscription_token tk{what.ptr()};
std::unique_lock<std::mutex> guard{m_mtx};
auto last = m_attachables.end();
auto i = std::find_if(m_attachables.begin(), last, pred);
if (i == last) {
if (detach_impl(tk, m_attachables_head, true, true) == 0) {
auto ptr = what->subscribe(address());
if (ptr) {
m_attachables.emplace_back(std::move(ptr));
attach_impl(ptr);
}
}
}
......@@ -87,9 +85,10 @@ void local_actor::leave(const group& what) {
std::vector<group> local_actor::joined_groups() const {
std::vector<group> result;
result.reserve(20);
std::unique_lock<std::mutex> guard{m_mtx};
for (auto& ptr : m_attachables) {
auto sptr = dynamic_cast<abstract_group::subscription*>(ptr.get());
for (attachable* i = m_attachables_head.get(); i != 0; i = i->next.get()) {
auto sptr = dynamic_cast<abstract_group::subscription*>(i);
if (sptr) {
result.emplace_back(sptr->group());
}
......
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