Add 'xo-refcnt/' from commit 'bcd86e5324'
git-subtree-dir: xo-refcnt git-subtree-mainline:ce2fc80bfdgit-subtree-split:bcd86e5324
This commit is contained in:
commit
2075b654af
17 changed files with 1278 additions and 0 deletions
80
xo-refcnt/.github/workflows/cmake-single-platform.yml
vendored
Normal file
80
xo-refcnt/.github/workflows/cmake-single-platform.yml
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage.
|
||||
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml
|
||||
name: CMake on a single platform
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install catch2
|
||||
# install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]]
|
||||
run: sudo apt-get install -y catch2
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: Clone xo-cmake
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/xo-cmake
|
||||
path: repo/xo-cmake
|
||||
|
||||
- name: Configure xo-cmake
|
||||
run: cmake -B ${{github.workspace}}/build_xo-cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/xo-cmake
|
||||
|
||||
- name: Build xo-cmake (trivial)
|
||||
run: cmake --build ${{github.workspace}}/build_xo-cmake --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Install xo-cmake
|
||||
run: cmake --install ${{github.workspace}}/build_xo-cmake
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: Clone indentlog
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/indentlog
|
||||
path: repo/indentlog
|
||||
|
||||
- name: Configure indentlog
|
||||
# configure cmake for indentlog in dedicated build directory.
|
||||
run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog
|
||||
|
||||
- name: Build indentlog
|
||||
run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Install indentlog
|
||||
# install into ${{github.workspace}}/local
|
||||
run: cmake --install ${{github.workspace}}/build_indentlog
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: Configure self (refcnt)
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Build refcnt
|
||||
# Build your program with the given configuration
|
||||
run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Test refcnt
|
||||
working-directory: ${{github.workspace}}/build_refcnt
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: ctest -C ${{env.BUILD_TYPE}}
|
||||
9
xo-refcnt/.gitignore
vendored
Normal file
9
xo-refcnt/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# emacs workspace config
|
||||
.projectile
|
||||
# symlink to ${mybuilddirectory}/compile_commands.json for LSP
|
||||
compile_commands.json
|
||||
# LSP keeps state here
|
||||
.cache
|
||||
# typical build directories
|
||||
.build*
|
||||
ccov
|
||||
31
xo-refcnt/CMakeLists.txt
Normal file
31
xo-refcnt/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# refcnt/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(refcnt VERSION 0.1)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(cmake/xo-bootstrap-macros.cmake)
|
||||
|
||||
xo_cxx_toplevel_options3()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# c++ settings
|
||||
|
||||
set(XO_PROJECT_NAME refcnt) # is this used?
|
||||
set(PROJECT_CXX_FLAGS "")
|
||||
#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only!
|
||||
|
||||
add_definitions(${PROJECT_CXX_FLAGS})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(utest)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# cmake export
|
||||
|
||||
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||
|
||||
# end CMakeLists.txt
|
||||
104
xo-refcnt/README.md
Normal file
104
xo-refcnt/README.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# intrusive reference counting
|
||||
|
||||
Refcnt is a small shared library supplying intrusive reference counting.
|
||||
|
||||
## Features
|
||||
|
||||
- base class `ref::Refcounted`.
|
||||
Application classes opt-in to reference counting by inheriting this class.
|
||||
- common base simplifies connecting to common-base-object applications such as python, java etc.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### build + install `xo-cmake` dependency (cmake macros)
|
||||
|
||||
see [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake)
|
||||
|
||||
Installs a few cmake ingredients, along with a build assistant for XO projects such as this one.
|
||||
|
||||
### build + install XO deps
|
||||
```
|
||||
$ xo-build --clone --configure --build --install xo-indentlog
|
||||
```
|
||||
|
||||
### copy `refcnt` repository locally
|
||||
```
|
||||
$ xo-build --clone xo-refcnt
|
||||
```
|
||||
|
||||
or equivalently
|
||||
```
|
||||
$ git clone git@github.com:Rconybea/refcnt.git xo-refcnt
|
||||
```
|
||||
|
||||
### build + install
|
||||
```
|
||||
$ xo-build --configure --build --install xo-refcnt
|
||||
```
|
||||
|
||||
or equivalently:
|
||||
```
|
||||
$ mkdir xo-refcnt/.build
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-refcnt -B xo-refcnt/.build
|
||||
$ cmake --build xo-refcnt/.build
|
||||
$ cmake --install xo-refcnt/.build
|
||||
```
|
||||
|
||||
`CMAKE_PREFIX_PATH` should point to the prefix where `xo-indentlog` is installed
|
||||
|
||||
alternatively, if you're a nix user:
|
||||
```
|
||||
$ git clone git@github.com:rconybea/xo-nix.git
|
||||
$ ls -d xo-nix
|
||||
xo-nix
|
||||
$ cd xo-nix
|
||||
$ nix-build -A xo-refcnt
|
||||
```
|
||||
|
||||
### build for unit test coverage
|
||||
```
|
||||
$ cmake -DCMAKE_BUILD_TYPE=coverage -DCMAKE_PREFIX_PATH=${PREFIX} -S xo-refcnt -B xo-refcnt/.build-ccov
|
||||
$ cmake --build xo-refcnt/.build-ccov
|
||||
```
|
||||
|
||||
### LSP support
|
||||
```
|
||||
$ cd xo-refcnt
|
||||
$ ln -s build/compile_commands.json # lsp will look for compile_commands.json in the root of the source tree
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### 1
|
||||
```
|
||||
#include "xo/refcnt/Refcounted.hpp"
|
||||
|
||||
using xo::ref::Refcounted;
|
||||
|
||||
struct MyObject : public Refcounted {
|
||||
static rp<MyObject> make() { return new MyObject(); }
|
||||
|
||||
private:
|
||||
// intrusively-reference-counted objects should only be heap-allocated
|
||||
MyObject() { ... }
|
||||
};
|
||||
|
||||
int main() {
|
||||
// create reference-counted instance
|
||||
auto x = MyObject::make();
|
||||
auto y = x;
|
||||
// x,y refer to the same instance.
|
||||
x = nullptr;
|
||||
// y holds last reference
|
||||
y = nullptr;
|
||||
// MyObject has been deleted
|
||||
}
|
||||
```
|
||||
|
||||
### 2
|
||||
|
||||
To log reference-counting activity
|
||||
|
||||
```
|
||||
xo::ref::intrusive_ptr_set_debug(true);
|
||||
```
|
||||
6
xo-refcnt/cmake/refcntConfig.cmake.in
Normal file
6
xo-refcnt/cmake/refcntConfig.cmake.in
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(indentlog)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
35
xo-refcnt/cmake/xo-bootstrap-macros.cmake
Normal file
35
xo-refcnt/cmake/xo-bootstrap-macros.cmake
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ----------------------------------------------------------------
|
||||
# for example:
|
||||
# $ PREFIX=/usr/local # for example
|
||||
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||
#
|
||||
# will get
|
||||
# CMAKE_MODULE_PATH
|
||||
# from xo-cmake-config --cmake-module-path
|
||||
#
|
||||
# and expect .cmake macros in
|
||||
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||
|
||||
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
|
||||
message(FATAL "could not find xo-cmake-config executable")
|
||||
endif()
|
||||
|
||||
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
|
||||
|
||||
if (NOT XO_SUBMODULE_BUILD)
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||
# default to typical install location for xo-project-macros
|
||||
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||
#
|
||||
include(xo_macros/xo_cxx)
|
||||
|
||||
xo_cxx_bootstrap_message()
|
||||
92
xo-refcnt/include/xo/cxxutil/demangle.hpp
Normal file
92
xo-refcnt/include/xo/cxxutil/demangle.hpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* @file demangle.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <array> // std::array
|
||||
#include <utility> // std::index_sequence
|
||||
|
||||
namespace xo {
|
||||
namespace reflect {
|
||||
|
||||
template <std::size_t...Idxs>
|
||||
constexpr auto
|
||||
substring_as_array(std::string_view str,
|
||||
std::index_sequence<Idxs...> indexes)
|
||||
{
|
||||
//return std::array<char, indexes.size()+1>{str[Idxs]..., '\n'};
|
||||
return std::array<char, indexes.size()>{str[Idxs]...};
|
||||
} /*substring_as_array*/
|
||||
|
||||
template <typename T> constexpr auto type_name_array() {
|
||||
#if defined(__clang__)
|
||||
constexpr auto prefix = std::string_view{"[T = "};
|
||||
constexpr auto suffix = std::string_view{"]"};
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(__GNUC__)
|
||||
constexpr auto prefix = std::string_view{"with T = "};
|
||||
constexpr auto suffix = std::string_view{"]"};
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(_MSC_VER)
|
||||
constexpr auto prefix = std::string_view{"type_name_array<"};
|
||||
constexpr auto suffix = std::string_view{">(void)"};
|
||||
constexpr auto function = std::string_view{__FUNCSIG__};
|
||||
#else
|
||||
# error type_name_array: Unsupported compiler
|
||||
#endif
|
||||
|
||||
constexpr auto start = function.find(prefix) + prefix.size();
|
||||
constexpr auto end = function.rfind(suffix);
|
||||
|
||||
//static_assert(start < end);
|
||||
|
||||
constexpr auto name = function.substr(start, (end - start));
|
||||
|
||||
constexpr auto ixseq = std::make_index_sequence<name.size()>{};
|
||||
|
||||
return substring_as_array(name, ixseq);
|
||||
} /*type_name_array*/
|
||||
|
||||
template <typename T>
|
||||
struct type_name_holder {
|
||||
static inline constexpr auto value = type_name_array<T>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr auto type_name() -> std::string_view
|
||||
{
|
||||
constexpr auto& value = type_name_holder<T>::value;
|
||||
return std::string_view{value.data(), value.size()};
|
||||
}
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
template <std::string_view const&... Strs>
|
||||
struct join
|
||||
{
|
||||
// Join all strings into a single std::array of chars
|
||||
static constexpr auto impl() noexcept
|
||||
{
|
||||
constexpr std::size_t len = (Strs.size() + ... + 0);
|
||||
std::array<char, len + 1> arr{};
|
||||
auto append = [i = 0, &arr](auto const& s) mutable {
|
||||
for (auto c : s) arr[i++] = c;
|
||||
};
|
||||
(append(Strs), ...);
|
||||
arr[len] = 0;
|
||||
return arr;
|
||||
}
|
||||
// Give the joined string static storage
|
||||
static constexpr auto arr = impl();
|
||||
// View as a std::string_view
|
||||
static constexpr std::string_view value {arr.data(), arr.size() - 1};
|
||||
};
|
||||
|
||||
// Helper to get the value out
|
||||
template <std::string_view const&... Strs>
|
||||
static constexpr auto join_v = join<Strs...>::value;
|
||||
#endif
|
||||
} /*namespace reflect*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end demangle.hpp */
|
||||
29
xo-refcnt/include/xo/refcnt/Displayable.hpp
Normal file
29
xo-refcnt/include/xo/refcnt/Displayable.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* @file Displayable.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Refcounted.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
class Displayable : public Refcount {
|
||||
public:
|
||||
/* write some kind of human-readable representation on stream */
|
||||
virtual void display(std::ostream & os) const = 0;
|
||||
std::string display_string() const;
|
||||
}; /*Displayable*/
|
||||
|
||||
/* see also
|
||||
* operator<<(std::ostream &, intrusive_ptr<T> const &)
|
||||
* in [Refcounted.hpp]
|
||||
*/
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &os, Displayable const & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Displayable.hpp */
|
||||
351
xo-refcnt/include/xo/refcnt/Refcounted.hpp
Normal file
351
xo-refcnt/include/xo/refcnt/Refcounted.hpp
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
/* @file Refcounted.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/cxxutil/demangle.hpp"
|
||||
|
||||
//#include <boost/intrusive_ptr.hpp>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
template <typename T>
|
||||
class intrusive_ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using rp = ref::intrusive_ptr<T>;
|
||||
|
||||
namespace ref {
|
||||
class Refcount;
|
||||
|
||||
template<typename T>
|
||||
class Borrow;
|
||||
|
||||
/* originally used boost::instrusive_ptr<>.
|
||||
* ran into a bug. probably mine, but implemented
|
||||
* refcounting inline for debugging
|
||||
*/
|
||||
template<typename T>
|
||||
class intrusive_ptr {
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
public:
|
||||
intrusive_ptr() : ptr_(nullptr) {}
|
||||
intrusive_ptr(T * x) : ptr_(x) {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_ctor(sc_self_type, this, x);
|
||||
#endif
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
} /*ctor*/
|
||||
|
||||
/* NOTE: need exactly this form for copy-constructor
|
||||
* clang11 will not recognize template form below as
|
||||
* supplying copy ctor, and default version is broken for
|
||||
* instrusive_ptr.
|
||||
*/
|
||||
intrusive_ptr(intrusive_ptr const & x) : ptr_(x.get()) {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_cctor(sc_self_type, this, x.get());
|
||||
#endif
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
} /*cctor*/
|
||||
|
||||
/* create from instrusive pointer to some related type S */
|
||||
template<typename S>
|
||||
intrusive_ptr(intrusive_ptr<S> const & x) : ptr_(x.get()) {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_cctor(sc_self_type, this, x.get());
|
||||
#endif
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
} /*cctor*/
|
||||
|
||||
/* move ctor -- in this case don't need to update refcount */
|
||||
intrusive_ptr(intrusive_ptr && x) : ptr_{std::move(x.ptr_)} {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_mctor(sc_self_type, this, ptr_);
|
||||
#endif
|
||||
/* since we're moving from x, need to make sure x dtor
|
||||
* doesn't decrement refcount
|
||||
*/
|
||||
x.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
/* aliasing ctor. see ctor (8) here:
|
||||
* [[https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr]]
|
||||
* and this dicsussion:
|
||||
* [[https://stackoverflow.com/questions/49178231/pybind11-multiple-inheritance-with-custom-holder-type-fails-to-cast-to-base-type/73131206#73131206]]
|
||||
*/
|
||||
template<typename Y>
|
||||
intrusive_ptr(intrusive_ptr<Y> const & /*x*/, element_type * y) : ptr_{y} {
|
||||
if (std::is_same<Y, element_type>()) {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_actor(sc_self_type, this, y);
|
||||
#endif
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
; /* trivial aliasing, proceed */
|
||||
} else {
|
||||
using xo::xtag;
|
||||
throw std::runtime_error(tostr("attempt to use aliasing ctor with",
|
||||
xtag("Y", reflect::type_name<Y>()),
|
||||
xtag("T", reflect::type_name<T>())));
|
||||
}
|
||||
} /*ctor*/
|
||||
|
||||
~intrusive_ptr() {
|
||||
T * x = this->ptr_;
|
||||
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_dtor(sc_self_type, this, x);
|
||||
#endif
|
||||
|
||||
this->ptr_ = nullptr;
|
||||
|
||||
intrusive_ptr_release(x);
|
||||
} /*dtor*/
|
||||
|
||||
static bool compare(intrusive_ptr<T> const & x,
|
||||
intrusive_ptr<T> const & y) {
|
||||
return ptrdiff_t(x.get() - y.get());
|
||||
}
|
||||
|
||||
Borrow<T> borrow() const;
|
||||
|
||||
T * get() const { return ptr_; }
|
||||
|
||||
T * operator->() const { return ptr_; }
|
||||
|
||||
operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
intrusive_ptr<T> & operator=(intrusive_ptr<T> const & rhs) {
|
||||
T * x = rhs.get();
|
||||
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_assign(sc_self_type, this, x);
|
||||
#endif
|
||||
|
||||
T * old = this->ptr_;
|
||||
this->ptr_ = x;
|
||||
|
||||
intrusive_ptr_add_ref(x);
|
||||
intrusive_ptr_release(old);
|
||||
|
||||
return *this;
|
||||
} /*operator=*/
|
||||
|
||||
intrusive_ptr<T> & operator=(intrusive_ptr<T> && rhs) {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
intrusive_ptr_log_massign(sc_self_type, this, rhs.get());
|
||||
#endif
|
||||
|
||||
std::swap(this->ptr_, rhs.ptr_);
|
||||
|
||||
/* dtor on rhs will decrement refcount on old value of this->ptr_
|
||||
* don't increment for new value, since refcount just transfers from rhs to *this
|
||||
*/
|
||||
|
||||
return *this;
|
||||
} /*operator=*/
|
||||
|
||||
private:
|
||||
static constexpr std::string_view sc_self_type = xo::reflect::type_name<intrusive_ptr<T>>();
|
||||
|
||||
private:
|
||||
T * ptr_ = nullptr;
|
||||
}; /*intrusive_ptr*/
|
||||
|
||||
template<typename T>
|
||||
inline bool operator==(intrusive_ptr<T> const & x,
|
||||
intrusive_ptr<T> const & y) { return intrusive_ptr<T>::compare(x, y) == 0; }
|
||||
|
||||
class Refcount {
|
||||
public:
|
||||
Refcount() : reference_counter_(0) {}
|
||||
/* WARNING: virtual dtor here is essential,
|
||||
* since it's what allows us to invoke delete on a Refcount*,
|
||||
* for an object of some derived class type T. Otherwise clang
|
||||
* will use different addresses for Refcount-part and T-part of
|
||||
* such instance, which means pointer given to delete will not be
|
||||
* the same as pointer returned from new
|
||||
*/
|
||||
virtual ~Refcount() = default;
|
||||
|
||||
uint32_t reference_counter() const { return reference_counter_.load(); }
|
||||
|
||||
private:
|
||||
friend uint32_t intrusive_ptr_refcount(Refcount *);
|
||||
friend void intrusive_ptr_add_ref(Refcount *);
|
||||
friend void intrusive_ptr_release(Refcount *);
|
||||
|
||||
private:
|
||||
std::atomic<uint32_t> reference_counter_;
|
||||
}; /*Refcount*/
|
||||
|
||||
inline uint32_t
|
||||
intrusive_ptr_refcount(Refcount * x) {
|
||||
/* reporting accurately for diagnostics */
|
||||
if (x)
|
||||
return x->reference_counter_.load();
|
||||
else
|
||||
return 0;
|
||||
} /*intrusive_ptr_refcount*/
|
||||
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
extern void intrusive_ptr_set_debug(bool x);
|
||||
extern void intrusive_ptr_log_ctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
/* here actor short for 'aliasing ctor' */
|
||||
extern void intrusive_ptr_log_actor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
extern void intrusive_ptr_log_cctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
extern void intrusive_ptr_log_mctor(std::string_view const & self_type,
|
||||
void *this_ptr,
|
||||
Refcount * x);
|
||||
extern void intrusive_ptr_log_dtor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
extern void intrusive_ptr_log_assign(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
extern void intrusive_ptr_log_massign(std::string_view const & self_type,
|
||||
void *this_ptr,
|
||||
Refcount * x);
|
||||
#endif
|
||||
extern void intrusive_ptr_add_ref(Refcount * x);
|
||||
extern void intrusive_ptr_release(Refcount * x);
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, intrusive_ptr<T> const & x) {
|
||||
if(x.get()) {
|
||||
os << *(x.get());
|
||||
} else {
|
||||
os << "<nullptr " << reflect::type_name<T>() << ">";
|
||||
}
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
|
||||
/** Wrap a (presumably non-reference-counted) class T so that it has a refcount.
|
||||
**/
|
||||
template <typename T>
|
||||
class RefcountWrapper : public Refcount, public T {
|
||||
public:
|
||||
template<typename... Args>
|
||||
RefcountWrapper(Args... args) : Refcount(), T(std::forward<Args...>(args...)) {}
|
||||
};
|
||||
|
||||
/* borrow a reference-counted pointer to pass down the stack
|
||||
* 1. borrowed pointer intended to replace:
|
||||
* a. code like
|
||||
* foo(rp<T> x),
|
||||
* passing rp<T> by value requires increment/decrement pair,
|
||||
* which is superfluous given that caller holds reference throughout
|
||||
* b. code like
|
||||
* foo(rp<T> const & x)
|
||||
* passing rp<T> by reference requires double-indirection in called
|
||||
* code
|
||||
* 2. borrowed pointer does not check/maintain reference count.
|
||||
* it should never be stored in a struct; intended strictly
|
||||
* to be passed down stack
|
||||
* 3. just the same, want to be able to copy the borrowed pointer,
|
||||
* to avoid double-indirection
|
||||
* 4. also can promote borrowed pointer to full reference-counted
|
||||
* whenever desired
|
||||
*/
|
||||
template<typename T>
|
||||
class Borrow {
|
||||
public:
|
||||
Borrow() = default;
|
||||
|
||||
template <typename S>
|
||||
Borrow(rp<S> const & x) : ptr_(x.get()) {}
|
||||
|
||||
template <typename S>
|
||||
Borrow(S * x) : ptr_(x) {}
|
||||
|
||||
Borrow(Borrow const & x) = default;
|
||||
|
||||
/* convert from another borrow, if it has compatible pointer type */
|
||||
template<typename S>
|
||||
Borrow(Borrow<S> const & x) : ptr_(x.get()) {}
|
||||
|
||||
/* dynamic cast from a pointer to an object of some convertible type */
|
||||
template<typename S>
|
||||
static Borrow<T> from(Borrow<S> x) {
|
||||
return Borrow(dynamic_cast<T *>(x.get()));
|
||||
} /*from*/
|
||||
|
||||
/* promote from native pointer */
|
||||
static Borrow<T> from_native(T * x) {
|
||||
return Borrow(x);
|
||||
} /*from_native*/
|
||||
|
||||
T * get() const { return ptr_; }
|
||||
|
||||
rp<T> promote() const { return rp<T>(ptr_); }
|
||||
|
||||
T & operator*() const { return *ptr_; }
|
||||
T * operator->() const { return ptr_; }
|
||||
|
||||
operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
static int32_t compare(Borrow const & x, Borrow const & y) {
|
||||
return ptrdiff_t(x.get() - y.get());
|
||||
} /*compare*/
|
||||
|
||||
static int32_t compare(rp<T> const & x, Borrow const & y) {
|
||||
return ptrdiff_t(x.get() - y.get());
|
||||
} /*compare*/
|
||||
|
||||
template <typename S>
|
||||
Borrow<T> & operator=(const Borrow<S> & x) {
|
||||
ptr_ = x.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Borrow& operator=(const Borrow& x) {
|
||||
ptr_ = x.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
T * ptr_ = nullptr;
|
||||
}; /*Borrow*/
|
||||
|
||||
template<typename T>
|
||||
inline bool operator==(Borrow<T> x, Borrow<T> y) { return Borrow<T>::compare(x, y) == 0; }
|
||||
|
||||
template<typename T>
|
||||
inline bool operator==(rp<T> const & x, Borrow<T> y) { return Borrow<T>::compare(x, y) == 0; }
|
||||
|
||||
template<typename T>
|
||||
using brw = Borrow<T>;
|
||||
|
||||
template<typename T>
|
||||
Borrow<T>
|
||||
intrusive_ptr<T>::borrow() const {
|
||||
return Borrow<T>(*this);
|
||||
} /*borrow*/
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, Borrow<T> x) {
|
||||
if (x) {
|
||||
os << *x;
|
||||
} else {
|
||||
os << "<nullptr " << reflect::type_name<T>() << ">";
|
||||
}
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Refcounted.hpp */
|
||||
28
xo-refcnt/include/xo/refcnt/Unowned.hpp
Normal file
28
xo-refcnt/include/xo/refcnt/Unowned.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* @file Unowned.hpp */
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
/* use this is a holder type for pointers that pybind11 should treat
|
||||
* as "not-my-problem". in particular that pybind11 should never delete.
|
||||
*/
|
||||
template<typename T>
|
||||
class unowned_ptr {
|
||||
public:
|
||||
unowned_ptr(T * x) : ptr_{x} {}
|
||||
unowned_ptr(unowned_ptr const & x) = default;
|
||||
~unowned_ptr() = default;
|
||||
|
||||
T * get() const { return ptr_; }
|
||||
T * operator->() const { return ptr_; }
|
||||
|
||||
operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
unowned_ptr<T> & operator=(unowned_ptr<T> const & rhs) = default;
|
||||
|
||||
private:
|
||||
T * ptr_ = nullptr;
|
||||
}; /*unowned_ptr*/
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Unowned.hpp */
|
||||
10
xo-refcnt/src/CMakeLists.txt
Normal file
10
xo-refcnt/src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
set(SELF_LIB refcnt)
|
||||
set(SELF_SRCS Refcounted.cpp Displayable.cpp)
|
||||
|
||||
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
|
||||
xo_install_include_tree3(include/xo/cxxutil)
|
||||
|
||||
# NOTE:
|
||||
# dependency set here must be kept consistent with refcnt/cmake/refcntConfig.cmake.in
|
||||
#
|
||||
xo_dependency(${SELF_LIB} indentlog)
|
||||
16
xo-refcnt/src/Displayable.cpp
Normal file
16
xo-refcnt/src/Displayable.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/* @file Displayable.cpp */
|
||||
|
||||
#include "Displayable.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::tostr;
|
||||
|
||||
namespace ref {
|
||||
std::string
|
||||
Displayable::display_string() const {
|
||||
return tostr(*this);
|
||||
} /*display_string*/
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Displayable.cpp */
|
||||
159
xo-refcnt/src/Refcounted.cpp
Normal file
159
xo-refcnt/src/Refcounted.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/* @file Refcounted.cpp */
|
||||
|
||||
#include "Refcounted.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING
|
||||
namespace {
|
||||
/* verbose logging for intrusive_ptr */
|
||||
static bool s_logging_enabled = false;
|
||||
|
||||
void
|
||||
intrusive_ptr_log_aux(std::string_view const & self_type,
|
||||
std::string_view const & method_name,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
scope lscope(XO_LITERAL(log_level::verbose, self_type, method_name),
|
||||
"enter",
|
||||
xtag("this", this_ptr),
|
||||
xtag("x", x),
|
||||
xtag("n", intrusive_ptr_refcount(x)));
|
||||
} /*intrusive_ptr_log_aux*/
|
||||
} /*namespace*/
|
||||
|
||||
void
|
||||
intrusive_ptr_set_debug(bool debug_flag) {
|
||||
s_logging_enabled = debug_flag;
|
||||
} /*intrusive_ptr_set_debug*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_ctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::ctor", this_ptr, x);
|
||||
} /*intrusive_ptr_log_ctor*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_actor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::actor", this_ptr, x);
|
||||
} /*intrusive_ptr_log_actor*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_cctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::cctor", this_ptr, x);
|
||||
} /*intrusive_ptr_log_cctor*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_mctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::mctor", this_ptr, x);
|
||||
} /*intrusive_ptr_log_mctor*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_dtor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::dtor", this_ptr, x);
|
||||
} /*intrusive_ptr_log_dtor*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_assign(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::=", this_ptr, x);
|
||||
} /*intrusive_ptr_log_assign*/
|
||||
|
||||
void
|
||||
intrusive_ptr_log_massign(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x)
|
||||
{
|
||||
if (s_logging_enabled)
|
||||
intrusive_ptr_log_aux(self_type, "::m=", this_ptr, x);
|
||||
} /*intrusive_ptr_log_massign*/
|
||||
#endif
|
||||
|
||||
void
|
||||
intrusive_ptr_add_ref(Refcount * x)
|
||||
{
|
||||
/* for adding reference -- can use relaxed order,
|
||||
* since any reordering of a set of intrusive_ptr_add_ref()
|
||||
* calls is ok, provided no intervening intrusive_ptr_release()
|
||||
* calls.
|
||||
*/
|
||||
bool success = (x == nullptr);
|
||||
|
||||
while(!success) {
|
||||
uint32_t n = x->reference_counter_.load(std::memory_order_relaxed);
|
||||
|
||||
success = x->reference_counter_.compare_exchange_strong(n, n+1,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
} /*intrusive_ptr_add_ref*/
|
||||
|
||||
void
|
||||
intrusive_ptr_release(Refcount * x)
|
||||
{
|
||||
using xo::scope;
|
||||
using xo::xtag;
|
||||
|
||||
scope log(XO_ENTER0(verbose),
|
||||
"enter",
|
||||
xtag("x", x),
|
||||
xtag("n", x ? x->reference_counter_.load() : 0));
|
||||
|
||||
/* for decrement, need acq_rel ordering */
|
||||
bool success = (x == nullptr);
|
||||
uint32_t n = 0;
|
||||
|
||||
while(!success) {
|
||||
n = x->reference_counter_.load(std::memory_order_acquire);
|
||||
|
||||
if(n == static_cast<uint32_t>(-1)) {
|
||||
log && log("detected double-delete attempt",
|
||||
xtag("x", x),
|
||||
xtag("n", n));
|
||||
assert(false);
|
||||
}
|
||||
|
||||
success = x->reference_counter_.compare_exchange_strong(n, n-1,
|
||||
std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
if(n == 1) {
|
||||
/* just deleted the last reference, so recover the object */
|
||||
|
||||
log && log("delete object with 0 refs");
|
||||
|
||||
/* for good measure: replace refcount with -1,
|
||||
* in hope of detecting a double-delete attempt
|
||||
*/
|
||||
x->reference_counter_.store(static_cast<uint32_t>(-1));
|
||||
|
||||
delete x;
|
||||
}
|
||||
} /*intrusive_ptr_release*/
|
||||
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Refcounted.cpp */
|
||||
15
xo-refcnt/utest/CMakeLists.txt
Normal file
15
xo-refcnt/utest/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# build unittest 'refcnt/utest/utest.refcnt
|
||||
|
||||
set(SELF_EXECUTABLE_NAME utest.refcnt)
|
||||
# These tests can use the Catch2-provided main
|
||||
set(SELF_SOURCE_FILES intrusive_ptr.test.cpp refcnt_utest_main.cpp)
|
||||
|
||||
xo_add_utest_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 3rd party dependency: catch2:
|
||||
|
||||
xo_self_dependency(${SELF_EXECUTABLE_NAME} refcnt)
|
||||
xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2)
|
||||
|
||||
# end CMakeLists.txt
|
||||
7
xo-refcnt/utest/README
Normal file
7
xo-refcnt/utest/README
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
* to run unit tests for this directoyr
|
||||
|
||||
$ cd path/to/kalman/build
|
||||
$ ./refcnt/utest/utest.refcnt
|
||||
|
||||
|
||||
|
||||
300
xo-refcnt/utest/intrusive_ptr.test.cpp
Normal file
300
xo-refcnt/utest/intrusive_ptr.test.cpp
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
/* @file intrusive_ptr.test.cpp */
|
||||
|
||||
#include "Refcounted.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace xo {
|
||||
using xo::ref::Refcount;
|
||||
using xo::ref::Borrow;
|
||||
using xo::ref::brw;
|
||||
using xo::ref::intrusive_ptr_refcount;
|
||||
using xo::ref::intrusive_ptr_add_ref;
|
||||
using xo::ref::intrusive_ptr_release;
|
||||
|
||||
namespace ut {
|
||||
namespace {
|
||||
static uint32_t ctor_count = 0;
|
||||
static uint32_t dtor_count = 0;
|
||||
|
||||
/* empty object, except for refcount */
|
||||
class JustRefcount : public ref::Refcount {
|
||||
public:
|
||||
JustRefcount() { ++ctor_count; }
|
||||
~JustRefcount() { ++dtor_count; }
|
||||
}; /*JustRefcount*/
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & os, JustRefcount & x) {
|
||||
os << "JustRefcount";
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
} /*namespace*/
|
||||
|
||||
TEST_CASE("refcount", "[refcnt][trivial]") {
|
||||
REQUIRE(std::is_default_constructible<ref::Refcount>() == true);
|
||||
REQUIRE(std::has_virtual_destructor<ref::Refcount>() == true);
|
||||
|
||||
/* refcount object self-initializes to 0 */
|
||||
Refcount x;
|
||||
REQUIRE(x.reference_counter() == 0);
|
||||
} /*TEST_CASE(refcount)*/
|
||||
|
||||
TEST_CASE("null-intrusive-ptr", "[refcnt][trivial]") {
|
||||
//constexpr std::string_view c_self = "TEST_CASE:null-intrusive-ptr";
|
||||
|
||||
REQUIRE(std::has_virtual_destructor<JustRefcount>() == true);
|
||||
|
||||
rp<JustRefcount> p1;
|
||||
rp<JustRefcount> p2;
|
||||
|
||||
REQUIRE(sizeof(p1) == sizeof(JustRefcount*));
|
||||
|
||||
REQUIRE(p1.get() == nullptr);
|
||||
REQUIRE(p1.operator->() == nullptr);
|
||||
|
||||
REQUIRE(p2.get() == nullptr);
|
||||
REQUIRE(p2.operator->() == nullptr);
|
||||
|
||||
/* can assign a nullptr */
|
||||
rp<JustRefcount> p3;
|
||||
|
||||
REQUIRE(p3.get() == nullptr);
|
||||
p3 = p1;
|
||||
REQUIRE(p3.get() == nullptr);
|
||||
|
||||
/* can use aux functions on null pointers */
|
||||
REQUIRE(intrusive_ptr_refcount(p1.get()) == 0);
|
||||
|
||||
intrusive_ptr_add_ref(nullptr);
|
||||
intrusive_ptr_release(nullptr);
|
||||
|
||||
/* can borrow a null intrusive_ptr */
|
||||
brw<JustRefcount> p1_brw = p1.borrow();
|
||||
brw<JustRefcount> p2_brw = p2.borrow();
|
||||
|
||||
REQUIRE(p1_brw.get() == nullptr);
|
||||
REQUIRE(p1_brw.operator->() == nullptr);
|
||||
/* null borrow is false-y */
|
||||
REQUIRE(p1_brw == false);
|
||||
|
||||
/* can promote a borrowed pointer */
|
||||
rp<JustRefcount> pp = p1_brw.promote();
|
||||
|
||||
REQUIRE(p1.get() == pp.get());
|
||||
|
||||
/* comparisons */
|
||||
REQUIRE(Borrow<JustRefcount>::compare(p1_brw, p2_brw) == 0);
|
||||
REQUIRE(p1_brw == p2_brw);
|
||||
REQUIRE((p1_brw != p2_brw) == false);
|
||||
REQUIRE(p1 == p1_brw);
|
||||
REQUIRE((p1 != p1_brw) == false);
|
||||
REQUIRE(p1_brw == p1);
|
||||
REQUIRE((p1_brw != p1) == false);
|
||||
} /*TEST_CASE(null-intrusive_ptr)*/
|
||||
|
||||
TEST_CASE("intrusive-ptr-identity", "[refcnt][identity]")
|
||||
{
|
||||
uint32_t cc = ctor_count;
|
||||
uint32_t dc = dtor_count;
|
||||
|
||||
rp<JustRefcount> p1(new JustRefcount());
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get() != nullptr);
|
||||
REQUIRE(p1.get() == p1.operator->());
|
||||
REQUIRE(intrusive_ptr_refcount(p1.get()) == 1);
|
||||
REQUIRE(p1->reference_counter() == 1);
|
||||
|
||||
intrusive_ptr_add_ref(p1.get());
|
||||
|
||||
REQUIRE(intrusive_ptr_refcount(p1.get()) == 2);
|
||||
|
||||
intrusive_ptr_release(p1.get());
|
||||
|
||||
REQUIRE(intrusive_ptr_refcount(p1.get()) == 1);
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
rp<JustRefcount> p2(new JustRefcount());
|
||||
|
||||
REQUIRE(ctor_count == cc + 2);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
REQUIRE(p2.get() != nullptr);
|
||||
REQUIRE(p2.get() != p1.get());
|
||||
REQUIRE(p2.get() == p2.operator->());
|
||||
REQUIRE(p2->reference_counter() == 1);
|
||||
|
||||
/* can borrow a non-null intrusive-ptr */
|
||||
brw<JustRefcount> p1_brw = p1.borrow();
|
||||
|
||||
REQUIRE(p1_brw.get() == p1.get());
|
||||
|
||||
/* borrowing does not change refcount, borrow not tracked */
|
||||
REQUIRE(ctor_count == cc + 2);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get()->reference_counter() == 1);
|
||||
|
||||
/* copying borrowed pointer does not touch refcount */
|
||||
brw<JustRefcount> p1_brw2 = p1_brw;
|
||||
|
||||
REQUIRE(ctor_count == cc + 2);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1_brw2.get() == p1.get());
|
||||
|
||||
REQUIRE(p1.get()->reference_counter() == 1);
|
||||
} /*TEST_CASE(identity-intrusive-ptr)*/
|
||||
|
||||
TEST_CASE("intrusive-ptr-release", "[refcnt][release]")
|
||||
{
|
||||
uint32_t cc = ctor_count;
|
||||
uint32_t dc = dtor_count;
|
||||
|
||||
rp<JustRefcount> p1(new JustRefcount());
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get() != nullptr);
|
||||
REQUIRE(p1->reference_counter() == 1);
|
||||
|
||||
/* reference count going to 0 -> delete object */
|
||||
p1 = nullptr;
|
||||
|
||||
REQUIRE(p1.get() == nullptr);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc + 1);
|
||||
} /*TEST_CASE(intrusive-ptr-release)*/
|
||||
|
||||
TEST_CASE("intrusive-ptr-copy", "[refcnt][copy]")
|
||||
{
|
||||
uint32_t cc = ctor_count;
|
||||
uint32_t dc = dtor_count;
|
||||
|
||||
rp<JustRefcount> p1(new JustRefcount());
|
||||
JustRefcount * p1_native = p1.get();
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get() != nullptr);
|
||||
REQUIRE(p1->reference_counter() == 1);
|
||||
|
||||
/* copy ctor ran to make copy of p1, did not allocate */
|
||||
rp<JustRefcount> p2(p1);
|
||||
|
||||
REQUIRE(p1->reference_counter() == 2);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
} /*TEST_CASE(intrusive-ptr-copy)*/
|
||||
|
||||
TEST_CASE("intrusive-ptr-move", "[refcnt][move]")
|
||||
{
|
||||
uint32_t cc = ctor_count;
|
||||
uint32_t dc = dtor_count;
|
||||
|
||||
rp<JustRefcount> p1(new JustRefcount());
|
||||
JustRefcount * p1_native = p1.get();
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get() != nullptr);
|
||||
REQUIRE(p1->reference_counter() == 1);
|
||||
|
||||
rp<JustRefcount> p2{std::move(p1)};
|
||||
|
||||
REQUIRE(p2->reference_counter() == 1);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p2 = nullptr;
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc + 1);
|
||||
} /*TEST_CASE(intrusive-ptr-move)*/
|
||||
|
||||
TEST_CASE("instrusive-ptr-assign", "[refcnt][assign]")
|
||||
{
|
||||
uint32_t cc = ctor_count;
|
||||
uint32_t dc = dtor_count;
|
||||
|
||||
rp<JustRefcount> p1(new JustRefcount());
|
||||
JustRefcount * p1_native = p1.get();
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get() != nullptr);
|
||||
REQUIRE(p1->reference_counter() == 1);
|
||||
|
||||
rp<JustRefcount> p2;
|
||||
|
||||
REQUIRE(p2.get() == nullptr);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p2 = p1;
|
||||
|
||||
REQUIRE(p2.get() == p1.get());
|
||||
REQUIRE(p2->reference_counter() == 2);
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p1 = nullptr;
|
||||
|
||||
REQUIRE(p2->reference_counter() == 1);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p2 = nullptr;
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc + 1);
|
||||
} /*TEST_CASE(intrusive-ptr-assign)*/
|
||||
|
||||
TEST_CASE("intrusive-ptr-move-assign", "[refcnt][move-assign]")
|
||||
{
|
||||
uint32_t cc = ctor_count;
|
||||
uint32_t dc = dtor_count;
|
||||
|
||||
rp<JustRefcount> p1(new JustRefcount());
|
||||
JustRefcount * p1_native = p1.get();
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
REQUIRE(p1.get() != nullptr);
|
||||
REQUIRE(p1->reference_counter() == 1);
|
||||
|
||||
rp<JustRefcount> p2;
|
||||
|
||||
REQUIRE(p2.get() == nullptr);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p2 = std::move(p1);
|
||||
|
||||
REQUIRE(p1.get() == nullptr);
|
||||
REQUIRE(p2.get() == p1_native);
|
||||
REQUIRE(p2->reference_counter() == 1);
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p1 = nullptr; /*no-op*/
|
||||
|
||||
REQUIRE(p2->reference_counter() == 1);
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc);
|
||||
|
||||
p2 = nullptr;
|
||||
|
||||
REQUIRE(ctor_count == cc + 1);
|
||||
REQUIRE(dtor_count == dc + 1);
|
||||
} /*TEST_CASE(intrusive-ptr-move-assign)*/
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end intrusive_ptr.test.cpp */
|
||||
6
xo-refcnt/utest/refcnt_utest_main.cpp
Normal file
6
xo-refcnt/utest/refcnt_utest_main.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* @file refcnt_utest_main.cpp */
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
/* end refcnt_utest_main.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue