Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 23 additions & 33 deletions include/crill/reclaim_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <thread>
#include <mutex>
#include <memory>
#include <algorithm>
#include <cassert>
#include <vector>
#include <crill/atomic_unique_ptr.h>

Expand Down Expand Up @@ -61,50 +63,37 @@ class reclaim_object
// Reading the value must happen through a reader class.
class reader;

// read_ptr provides scoped read access to the value.
class read_ptr
// custom deleter for read-only unique_ptr
struct read_ptr_deleter
{
public:
read_ptr(reader& rdr) noexcept
: rdr(rdr)
constexpr read_ptr_deleter() noexcept = default;
explicit constexpr read_ptr_deleter(reader& rdr) noexcept : rdr(&rdr)
{
assert(rdr.min_epoch == 0);

rdr.min_epoch.store(rdr.obj.current_epoch.load());
assert(rdr.min_epoch =! 0);
};

value_read = rdr.obj.value.load();
assert(value_read);
}

~read_ptr()
{
assert(rdr.min_epoch != 0);
rdr.min_epoch.store(0);
}
read_ptr_deleter(const read_ptr_deleter &) noexcept = default;
read_ptr_deleter(read_ptr_deleter &&) noexcept = default;
read_ptr_deleter &operator=(read_ptr_deleter const &) noexcept = default;
read_ptr_deleter &operator=(read_ptr_deleter &&) noexcept = default;

const T& operator*() const
void operator()(const T*) const
{
assert(value_read);
return *value_read;
if (rdr)
{
assert(rdr->min_epoch != 0);
rdr->min_epoch.store(0);
}
}

const T* operator->() const
{
assert(value_read);
return value_read;
}

read_ptr(read_ptr&&) = delete;
read_ptr& operator=(read_ptr&&) = delete;
read_ptr(const read_ptr&) = delete;
read_ptr& operator=(const read_ptr&) = delete;

private:
reader& rdr;
T* value_read = nullptr;
reader* rdr;
};

using read_ptr = std::unique_ptr<const T, read_ptr_deleter>;

class reader
{
public:
Expand All @@ -130,12 +119,13 @@ class reclaim_object
// Non-blocking guarantees: wait-free.
read_ptr read_lock() noexcept
{
return read_ptr(*this);
auto deleter = read_ptr_deleter(*this);
return read_ptr(obj.value.load(), std::move(deleter));
}

private:
friend class reclaim_object;
friend class read_ptr;
friend class read_ptr_deleter;
reclaim_object& obj;
std::atomic<std::uint64_t> min_epoch = 0;
};
Expand Down
2 changes: 2 additions & 0 deletions include/crill/reclaim_on_write_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <mutex>
#include <array>
#include <vector>
#include <cassert>
#include <algorithm>

namespace crill
{
Expand Down
11 changes: 8 additions & 3 deletions tests/reclaim_object_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,18 @@ TEST_CASE("reclaim_object::read_ptr")
crill::reclaim_object<std::string> obj(3, 'x');
auto reader = obj.get_reader();

SUBCASE("read_ptr is not copyable or movable")
SUBCASE("read_ptr is not copyable")
{
auto read_ptr = reader.read_lock();
static_assert(!std::is_copy_constructible_v<decltype(read_ptr)>);
static_assert(!std::is_copy_assignable_v<decltype(read_ptr)>);
static_assert(!std::is_move_constructible_v<decltype(read_ptr)>);
static_assert(!std::is_move_assignable_v<decltype(read_ptr)>);
}

SUBCASE("read_ptr is movable movable")
{
auto read_ptr = reader.read_lock();
static_assert(std::is_move_constructible_v<decltype(read_ptr)>);
static_assert(std::is_move_assignable_v<decltype(read_ptr)>);
}

SUBCASE("Dereference")
Expand Down
1 change: 1 addition & 0 deletions tests/utility_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <vector>
#include <thread>
#include <atomic>
#include <crill/utility.h>
#include "tests.h"

Expand Down