Commit 1700bcf8 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Fix copy-on-write semantic for messages

parent 8a47221b
......@@ -156,7 +156,8 @@ actor_factory make_actor_factory(F fun) {
typename ctrait::arg_types>;
fd f{fun, static_cast<impl*>(x)};
empty_type_erased_tuple dummy_;
auto& ct = msg.empty() ? dummy_ : const_cast<message&>(msg).content();
auto& ct = msg.empty() ? dummy_
: const_cast<message&>(msg).content();
auto opt = ct.apply(f);
if (!opt)
return {};
......
......@@ -122,7 +122,7 @@ public:
}
inline explicit operator bool() const noexcept {
return static_cast<bool>(ptr_);
return ptr_ != nullptr;
}
inline message_data* get() const noexcept {
......@@ -134,6 +134,22 @@ private:
intrusive_ptr<message_data> ptr_;
};
inline bool operator==(const message_data::cow_ptr& x, std::nullptr_t) {
return x.get() == nullptr;
}
inline bool operator==(std::nullptr_t, const message_data::cow_ptr& x) {
return x.get() == nullptr;
}
inline bool operator!=(const message_data::cow_ptr& x, std::nullptr_t) {
return x.get() != nullptr;
}
inline bool operator!=(std::nullptr_t, const message_data::cow_ptr& x) {
return x.get() != nullptr;
}
} // namespace detail
} // namespace caf
......
......@@ -23,28 +23,29 @@
#include <sstream>
#include <type_traits>
#include "caf/fwd.hpp"
#include "caf/skip.hpp"
#include "caf/atom.hpp"
#include "caf/none.hpp"
#include "caf/config.hpp"
#include "caf/optional.hpp"
#include "caf/make_counted.hpp"
#include "caf/fwd.hpp"
#include "caf/index_mapping.hpp"
#include "caf/make_counted.hpp"
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/skip.hpp"
#include "caf/type_nr.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/message_data.hpp"
#include "caf/detail/implicit_conversions.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/message_data.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
class message_handler;
/// Describes a fixed-length, copy-on-write, type-erased
/// tuple with elements of any type.
class message {
class message : public type_erased_tuple {
public:
// -- nested types -----------------------------------------------------------
......@@ -74,7 +75,33 @@ public:
message& operator=(message&&) noexcept;
explicit message(data_ptr ptr) noexcept;
~message();
~message() override;
// -- implementation of type_erased_tuple ------------------------------------
void* get_mutable(size_t p) override;
error load(size_t pos, deserializer& source) override;
size_t size() const noexcept override;
uint32_t type_token() const noexcept override;
rtti_pair type(size_t pos) const noexcept override;
const void* get(size_t pos) const noexcept override;
std::string stringify(size_t pos) const override;
type_erased_value_ptr copy(size_t pos) const override;
error save(size_t pos, serializer& sink) const override;
bool shared() const noexcept override;
error load(deserializer& source) override;
error save(serializer& sink) const override;
// -- factories --------------------------------------------------------------
......@@ -92,16 +119,6 @@ public:
/// Concatenates `*this` and `x`.
message& operator+=(const message& x);
/// Returns a mutable pointer to the element at position `p`.
void* get_mutable(size_t p);
/// Returns the value at position `p` as mutable reference of type `T`.
template <class T>
T& get_mutable_as(size_t p) {
CAF_ASSERT(match_element(p, type_nr<T>::value, &typeid(T)));
return *reinterpret_cast<T*>(get_mutable(p));
}
/// Returns `handler(*this)`.
optional<message> apply(message_handler handler);
......@@ -212,7 +229,7 @@ public:
/// Returns a const pointer to the element at position `p`.
inline const void* at(size_t p) const noexcept {
CAF_ASSERT(vals_);
CAF_ASSERT(vals_ != nullptr);
return vals_->get(p);
}
......@@ -226,21 +243,7 @@ public:
return vals_;
}
/// Returns a type hint for the pattern matching engine.
inline uint32_t type_token() const noexcept {
return vals_ ? vals_->type_token() : 0xFFFFFFFF;
}
/// Returns whether there are more than one references to the content.
inline bool shared() const noexcept {
return vals_ ? vals_->shared() : false;
}
/// Returns the size of this message.
inline size_t size() const noexcept {
return vals_ ? vals_->size() : 0;
}
/// Creates a new message from the first n values.
inline message take(size_t n) const {
return n >= size() ? *this : drop_right(size() - n);
......@@ -251,71 +254,24 @@ public:
return n >= size() ? *this : drop(size() - n);
}
/// Returns true if `size() == 0, otherwise false.
inline bool empty() const {
return size() == 0;
}
/// Returns the value at position `p` as const reference of type `T`.
template <class T>
const T& get_as(size_t p) const {
CAF_ASSERT(match_element(p, type_nr<T>::value, &typeid(T)));
return *reinterpret_cast<const T*>(at(p));
}
/// Queries whether the element at position `p` is of type `T`.
template <class T>
bool match_element(size_t p) const noexcept {
return vals_ ? vals_->match_element<T>(p) : false;
}
/// Queries whether the types of this message are `Ts...`.
template <class... Ts>
bool match_elements() const noexcept {
detail::type_list<Ts...> token;
return match_elements(token);
}
/// Queries the run-time type information for the element at position `pos`.
inline std::pair<uint16_t, const std::type_info*>
type(size_t pos) const noexcept {
CAF_ASSERT(vals_ && vals_->size() > pos);
return vals_->type(pos);
}
/// Checks whether the type of the stored value at position `pos`
/// matches type number `n` and run-time type information `p`.
bool match_element(size_t pos, uint16_t n,
const std::type_info* p) const noexcept {
CAF_ASSERT(vals_);
return vals_->matches(pos, n, p);
}
inline bool match_elements(detail::type_list<>) const noexcept {
return !vals_ || vals_->empty();
}
template <class T, class... Ts>
bool match_elements(detail::type_list<T, Ts...>) const noexcept {
return vals_ ? vals_->match_elements<T, Ts...>() : false;
}
/// @cond PRIVATE
/// @pre `!empty()`
inline type_erased_tuple& content() {
CAF_ASSERT(vals_);
CAF_ASSERT(vals_ != nullptr);
return *vals_;
}
inline const type_erased_tuple& content() const {
CAF_ASSERT(vals_);
CAF_ASSERT(vals_ != nullptr);
return *vals_;
}
/// @endcond
private:
// -- private helpers --------------------------------------------------------
template <size_t P>
static bool match_elements_impl(std::integral_constant<size_t, P>,
detail::type_list<>) noexcept {
......@@ -334,17 +290,24 @@ private:
static message concat_impl(std::initializer_list<data_ptr> xs);
// -- member functions -------------------------------------------------------
data_ptr vals_;
};
/// @relates message
error inspect(serializer& sink, message& msg);
// -- nested types -------------------------------------------------------------
/// @relates message
error inspect(deserializer& source, message& msg);
/// @relates message
std::string to_string(const message& msg);
/// Stores the result of `message::extract_opts`.
struct message::cli_res {
/// Stores the remaining (unmatched) arguments.
message remainder;
/// Stores the names of all active options.
std::set<std::string> opts;
/// Stores the automatically generated help text.
std::string helptext;
/// Stores errors during option parsing.
std::string error;
};
/// Stores the name of a command line option ("<long name>[,<short name>]")
/// along with a description and a callback.
......@@ -389,11 +352,7 @@ struct message::cli_arg {
/// Creates a CLI argument for converting from strings,
/// storing its matched argument in `dest`.
template <class T>
cli_arg(typename std::enable_if<
type_nr<T>::value != 0,
std::string
>::type nstr,
std::string tstr, T& arg)
cli_arg(std::string nstr, std::string tstr, T& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
flag(nullptr) {
......@@ -420,7 +379,7 @@ struct message::cli_arg {
flag(nullptr) {
fun = [&arg](const std::string& str) -> bool {
T x;
std::istringstream iss{ str };
std::istringstream iss{str};
if (iss >> x) {
arg.emplace_back(std::move(x));
return true;
......@@ -430,17 +389,16 @@ struct message::cli_arg {
}
};
/// Stores the result of `message::extract_opts`.
struct message::cli_res {
/// Stores the remaining (unmatched) arguments.
message remainder;
/// Stores the names of all active options.
std::set<std::string> opts;
/// Stores the automatically generated help text.
std::string helptext;
/// Stores errors during option parsing.
std::string error;
};
// -- related non-members ------------------------------------------------------
/// @relates message
error inspect(serializer& sink, message& msg);
/// @relates message
error inspect(deserializer& source, message& msg);
/// @relates message
std::string to_string(const message& msg);
/// @relates message
inline message operator+(const message& lhs, const message& rhs) {
......
......@@ -36,6 +36,8 @@
#include "caf/response_handle.hpp"
#include "caf/response_type.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
namespace caf {
namespace mixin {
......
......@@ -138,14 +138,8 @@ protected:
template <class F>
bool handle_system_message(mailbox_element& x, execution_unit* context,
bool trap_exit, F& down_msg_handler) {
auto& content = x.content();
if (content.type_token() == make_type_token<down_msg>()) {
if (content.shared()) {
auto vptr = content.copy(0);
down_msg_handler(vptr->get_mutable_as<down_msg>());
} else {
down_msg_handler(content.get_mutable_as<down_msg>(0));
}
if (x.content().type_token() == make_type_token<down_msg>()) {
down_msg_handler(x.content().get_mutable_as<down_msg>(0));
return true;
}
return handle_system_message(x, context, trap_exit);
......
......@@ -823,6 +823,8 @@ public:
}
*/
void push_to_cache(mailbox_element_ptr ptr);
/// @endcond
/// Returns the queue for storing incoming messages.
......@@ -865,8 +867,6 @@ protected:
bool handle_stream_msg(mailbox_element& x, behavior* active_behavior);
void push_to_cache(mailbox_element_ptr ptr);
// -- Member Variables -------------------------------------------------------
// used by both event-based and blocking actors
......
......@@ -60,7 +60,7 @@ public:
// -- modifiers --------------------------------------------------------------
/// Load the content for the tuple from `source`.
error load(deserializer& source);
virtual error load(deserializer& source);
// -- pure virtual observers -------------------------------------------------
......@@ -99,11 +99,12 @@ public:
std::string stringify() const;
/// Saves the content of the tuple to `sink`.
error save(serializer& sink) const;
virtual error save(serializer& sink) const;
/// Checks whether the type of the stored value at position `pos`
/// matches type number `n` and run-time type information `p`.
bool matches(size_t pos, uint16_t nr, const std::type_info* ptr) const noexcept;
bool matches(size_t pos, uint16_t nr,
const std::type_info* ptr) const noexcept;
// -- convenience functions --------------------------------------------------
......@@ -175,9 +176,8 @@ public:
/// Returns `true` if the pattern `Ts...` matches the content of this tuple.
template <class... Ts>
bool match_elements() const noexcept {
detail::meta_elements<detail::type_list<Ts...>> xs;
return xs.arr.empty() ? empty()
: detail::try_match(*this, &xs.arr[0], sizeof...(Ts));
detail::type_list<Ts...> tk;
return match_elements(tk);
}
template <class F>
......@@ -190,6 +190,16 @@ public:
}
private:
template <class T, class... Ts>
bool match_elements(detail::type_list<T, Ts...>) const noexcept {
detail::meta_elements<detail::type_list<T, Ts...>> xs;
return detail::try_match(*this, &xs.arr[0], 1 + sizeof...(Ts));
}
inline bool match_elements(detail::type_list<>) const noexcept {
return empty();
}
template <class F, class R, class... Ts>
optional<R> apply(F& fun, detail::type_list<R>,
detail::type_list<Ts...> tk) {
......
......@@ -50,7 +50,7 @@ public:
}
type_erased_tuple& content() override {
return msg_.content();
return msg_;
}
const type_erased_tuple& content() const override {
......
This diff is collapsed.
......@@ -37,19 +37,19 @@ using namespace caf;
namespace {
template <class... Ts>
optional<tuple<Ts...>> fetch(type_erased_tuple& x) {
optional<tuple<Ts...>> fetch(const type_erased_tuple& x) {
if (!x.match_elements<Ts...>())
return none;
return x.get_as_tuple<Ts...>();
}
template <class... Ts>
optional<tuple<Ts...>> fetch(message& x) {
optional<tuple<Ts...>> fetch(const message& x) {
return fetch<Ts...>(x.content());
}
template <class... Ts>
optional<tuple<Ts...>> fetch(mailbox_element& x) {
optional<tuple<Ts...>> fetch(const mailbox_element& x) {
return fetch<Ts...>(x.content());
}
......
......@@ -86,10 +86,48 @@ struct fixture {
actor_system system{cfg};
};
struct fail_on_copy {
int value;
fail_on_copy(int x = 0) : value(x) {
// nop
}
fail_on_copy(fail_on_copy&&) = default;
fail_on_copy& operator=(fail_on_copy&&) = default;
fail_on_copy(const fail_on_copy&) {
CAF_FAIL("fail_on_copy: copy constructor called");
}
fail_on_copy& operator=(const fail_on_copy&) {
CAF_FAIL("fail_on_copy: copy assign operator called");
}
};
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, fail_on_copy& x) {
return f(x.value);
}
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(message_lifetime_tests, fixture)
CAF_TEST(nocopy_in_scoped_actor) {
auto msg = make_message(fail_on_copy{1});
scoped_actor self{system};
self->send(self, msg);
self->receive(
[&](const fail_on_copy& x) {
CAF_CHECK_EQUAL(x.value, 1);
CAF_CHECK_EQUAL(msg.cvals()->get_reference_count(), 2u);
}
);
CAF_CHECK_EQUAL(msg.cvals()->get_reference_count(), 1u);
}
CAF_TEST(message_lifetime_in_scoped_actor) {
auto msg = make_message(1, 2, 3);
scoped_actor self{system};
......
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