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

Make COW pointer implementation publicly available

parent 9966da79
......@@ -70,9 +70,11 @@ public:
error save(size_t pos, serializer& sink) const override;
// -- observers --------------------------------------------------------------
// -- element access ---------------------------------------------------------
std::pair<message_data*, size_t> select(size_t pos) const;
std::pair<message_data*, size_t> select(size_t pos);
std::pair<const message_data*, size_t> select(size_t pos) const;
private:
// -- data members -----------------------------------------------------------
......
......@@ -23,10 +23,11 @@
#include <typeinfo>
#include "caf/fwd.hpp"
#include "caf/config.hpp"
#include "caf/ref_counted.hpp"
#include "caf/fwd.hpp"
#include "caf/intrusive_cow_ptr.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/ref_counted.hpp"
#include "caf/type_erased_tuple.hpp"
#include "caf/detail/type_list.hpp"
......@@ -38,7 +39,7 @@ class message_data : public ref_counted, public type_erased_tuple {
public:
// -- nested types -----------------------------------------------------------
class cow_ptr;
using cow_ptr = intrusive_cow_ptr<message_data>;
// -- constructors, destructors, and assignment operators --------------------
......@@ -58,97 +59,6 @@ public:
bool shared() const noexcept override;
};
class message_data::cow_ptr {
public:
// -- constructors, destructors, and assignment operators ------------------
cow_ptr() noexcept = default;
cow_ptr(cow_ptr&&) noexcept = default;
cow_ptr(const cow_ptr&) noexcept = default;
cow_ptr& operator=(cow_ptr&&) noexcept = default;
cow_ptr& operator=(const cow_ptr&) noexcept = default;
template <class T>
cow_ptr(intrusive_ptr<T> p) noexcept : ptr_(std::move(p)) {
// nop
}
inline cow_ptr(message_data* ptr, bool add_ref) noexcept
: ptr_(ptr, add_ref) {
// nop
}
// -- modifiers ------------------------------------------------------------
inline void swap(cow_ptr& other) noexcept {
ptr_.swap(other.ptr_);
}
inline void reset(message_data* p = nullptr, bool add_ref = true) noexcept {
ptr_.reset(p, add_ref);
}
inline message_data* release() noexcept {
return ptr_.detach();
}
inline void unshare() {
static_cast<void>(get_unshared());
}
inline message_data* operator->() {
return get_unshared();
}
inline message_data& operator*() {
return *get_unshared();
}
/// Returns the raw pointer. Callers are responsible for unsharing
/// the content if necessary.
inline message_data* raw_ptr() {
return ptr_.get();
}
// -- observers ------------------------------------------------------------
inline const message_data* operator->() const noexcept {
return ptr_.get();
}
inline const message_data& operator*() const noexcept {
return *ptr_;
}
inline explicit operator bool() const noexcept {
return ptr_ != nullptr;
}
inline message_data* get() const noexcept {
return ptr_.get();
}
private:
message_data* get_unshared();
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
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include "caf/intrusive_ptr.hpp"
namespace caf {
/// An intrusive, reference counting smart pointer implementation with
/// copy-on-write optimization.
/// @relates ref_counted
/// @relates intrusive_ptr
template <class T>
class intrusive_cow_ptr
: detail::comparable<intrusive_cow_ptr<T>>,
detail::comparable<intrusive_cow_ptr<T>, T*>,
detail::comparable<intrusive_cow_ptr<T>, std::nullptr_t>,
detail::comparable<intrusive_cow_ptr<T>, intrusive_ptr<T>> {
public:
// -- member types -----------------------------------------------------------
using pointer = T*;
using const_pointer = const T*;
using element_type = T;
using reference = T&;
using const_reference = const T&;
using counting_pointer = intrusive_ptr<T>;
// -- constructors, destructors, and assignment operators ------------------
intrusive_cow_ptr() noexcept = default;
intrusive_cow_ptr(intrusive_cow_ptr&&) noexcept = default;
intrusive_cow_ptr(const intrusive_cow_ptr&) noexcept = default;
explicit intrusive_cow_ptr(counting_pointer p) noexcept : ptr_(std::move(p)) {
// nop
}
explicit intrusive_cow_ptr(pointer ptr, bool add_ref = true) noexcept
: ptr_(ptr, add_ref) {
// nop
}
intrusive_cow_ptr& operator=(intrusive_cow_ptr&&) noexcept = default;
intrusive_cow_ptr& operator=(const intrusive_cow_ptr&) noexcept = default;
intrusive_cow_ptr& operator=(counting_pointer x) noexcept {
ptr_ = std::move(x);
return *this;
}
// -- comparison -----------------------------------------------------------
ptrdiff_t compare(std::nullptr_t) const noexcept {
return reinterpret_cast<ptrdiff_t>(get());
}
ptrdiff_t compare(const_pointer ptr) const noexcept {
return static_cast<ptrdiff_t>(get() - ptr);
}
ptrdiff_t compare(const counting_pointer& other) const noexcept {
return compare(other.get());
}
ptrdiff_t compare(const intrusive_cow_ptr& other) const noexcept {
return compare(other.get());
}
// -- modifiers ------------------------------------------------------------
/// Swaps the managed object with `other`.
void swap(intrusive_cow_ptr& other) noexcept {
ptr_.swap(other.ptr_);
}
/// Replaces the managed object.
void reset(pointer p = nullptr, bool add_ref = true) noexcept {
ptr_.reset(p, add_ref);
}
/// Returns the raw pointer without modifying reference
/// count and sets this to `nullptr`.
pointer detach() noexcept {
return ptr_.detach();
}
/// Returns the raw pointer without modifying reference
/// count and sets this to `nullptr`.
pointer release() noexcept {
return ptr_.release();
}
/// Forces a copy of the managed object unless it already has a reference
/// count of exactly 1.
void unshare() {
if (ptr_ != nullptr)
static_cast<void>(get_unshared());
}
/// Returns a mutable pointer to the managed object.
pointer operator->() {
return get_unshared();
}
/// Returns a mutable reference to the managed object.
reference operator*() {
return *get_unshared();
}
/// Returns a mutable pointer to the managed object.
pointer get_unshared() {
auto p = ptr_.get();
if (!p->unique()) {
copy_reset(p->copy());
return ptr_.get();
}
return p;
}
// -- observers ------------------------------------------------------------
/// Returns a read-only pointer to the managed object.
const_pointer get() const noexcept {
return ptr_.get();
}
/// Returns the intrusive pointer managing the object.
const counting_pointer& counting_ptr() const noexcept {
return ptr_;
}
/// Returns a read-only pointer to the managed object.
const_pointer operator->() const noexcept {
return get();
}
/// Returns a read-only reference to the managed object.
const_reference operator*() const noexcept {
return *get();
}
explicit operator bool() const noexcept {
return get() != nullptr;
}
private:
// -- allow T::copy to return raw, intrusive, or COW pointer -----------------
void copy_reset(pointer x) {
// Reference counted objects start with a reference count of 1.
reset(x, false);
}
void copy_reset(counting_pointer x) {
ptr_.swap(x);
}
void copy_reset(intrusive_cow_ptr x) {
swap(x);
}
// -- member vairables -------------------------------------------------------
counting_pointer ptr_;
};
/// @relates intrusive_cow_ptr
template <class T>
std::string to_string(const intrusive_cow_ptr<T>& x) {
return to_string(x.counting_ptr());
}
} // namespace caf
......@@ -107,7 +107,20 @@ error concatenated_tuple::save(size_t pos, serializer& sink) const {
return selected.first->save(selected.second, sink);
}
std::pair<message_data*, size_t> concatenated_tuple::select(size_t pos) const {
std::pair<message_data*, size_t> concatenated_tuple::select(size_t pos) {
auto idx = pos;
for (auto& m : data_) {
auto s = m->size();
if (idx >= s)
idx -= s;
else
return {m.get_unshared(), idx};
}
CAF_RAISE_ERROR(std::out_of_range, "concatenated_tuple::select out of range");
}
std::pair<const message_data*, size_t>
concatenated_tuple::select(size_t pos) const {
auto idx = pos;
for (const auto& m : data_) {
auto s = m->size();
......
......@@ -45,7 +45,8 @@ decorated_tuple::cow_ptr decorated_tuple::make(cow_ptr d, vector_type v) {
for (auto& i : v)
i = pmap[i];
}
return make_counted<decorated_tuple>(std::move(d), std::move(v));
auto res = make_counted<decorated_tuple>(std::move(d), std::move(v));
return decorated_tuple::cow_ptr{res};
}
message_data::cow_ptr decorated_tuple::copy() const {
......
......@@ -49,7 +49,7 @@ dynamic_message_data::~dynamic_message_data() {
}
message_data::cow_ptr dynamic_message_data::copy() const {
return make_counted<dynamic_message_data>(*this);
return message_data::cow_ptr{make_counted<dynamic_message_data>(*this)};
}
void* dynamic_message_data::get_mutable(size_t pos) {
......
......@@ -42,8 +42,8 @@ public:
: mailbox_element(std::move(x0), dynamic_category_correction(x3, x1),
std::move(x2)),
msg_(std::move(x3)) {
/// Make sure that `content` can access the raw pointer safely.
if (msg_.vals().raw_ptr() == nullptr) {
/// Make sure that `content` can access the pointer safely.
if (msg_.vals() == nullptr) {
message_builder mb;
msg_ = mb.to_message();
}
......
......@@ -151,7 +151,7 @@ error message::load(deserializer& source) {
err = source.end_object();
if (err)
return err;
message result{std::move(dmd)};
message result{detail::message_data::cow_ptr{std::move(dmd)}};
swap(result);
return none;
}
......
......@@ -31,15 +31,5 @@ bool message_data::shared() const noexcept {
return !unique();
}
message_data* message_data::cow_ptr::get_unshared() {
auto p = ptr_.get();
if (!p->unique()) {
auto cptr = p->copy();
ptr_.swap(cptr.ptr_);
return ptr_.get();
}
return p;
}
} // namespace detail
} // namespace caf
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