Skip to content

Commit

Permalink
Merge pull request CesiumGS#806 from CesiumGS/ref-counted-thread-safe
Browse files Browse the repository at this point in the history
Add ReferenceCountedThreadSafe class
  • Loading branch information
csciguy8 authored Feb 26, 2024
2 parents 5f5f726 + b723de2 commit e443a65
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 88 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
##### Breaking Changes :mega:

- Removed support for `EXT_feature_metadata` in `CesiumGltf`, `CesiumGltfReader`, and `CesiumGltfWriter`. This extension was replaced by `EXT_mesh_features`, `EXT_instance_features`, and `EXT_structural_metadata`.
- Moved `ReferenceCountedNonThreadSafe<T>` to `ReferenceCounted.h`. It also now a type alias for `ReferenceCounted<T, false>` rather than an actual class.

##### Additions :tada:

Expand All @@ -13,6 +14,7 @@
- Added `getTextureTransform` methods to `FeatureIdTextureView` and `PropertyTexturePropertyView`.
- Added `contains` method to `BoundingSphere`.
- Added `GlobeRectangle::MAXIMUM` static field.
- Added `ReferenceCountedThreadSafe` type alias.

##### Fixes :wrench:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <CesiumRasterOverlays/RasterOverlay.h>
#include <CesiumRasterOverlays/RasterOverlayTileProvider.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCountedNonThreadSafe.h>
#include <CesiumUtility/ReferenceCounted.h>
#include <CesiumUtility/Tracing.h>

#include <gsl/span>
Expand Down
2 changes: 1 addition & 1 deletion Cesium3DTilesSelection/src/TilesetContentManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include <Cesium3DTilesSelection/TilesetOptions.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/ReferenceCountedNonThreadSafe.h>
#include <CesiumUtility/ReferenceCounted.h>

#include <vector>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumGltf/Ktx2TranscodeTargets.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCountedNonThreadSafe.h>
#include <CesiumUtility/ReferenceCounted.h>

#include <nonstd/expected.hpp>
#include <spdlog/fwd.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <CesiumGeometry/Rectangle.h>
#include <CesiumGltf/Model.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCountedNonThreadSafe.h>
#include <CesiumUtility/ReferenceCounted.h>

#include <vector>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include <CesiumGltfReader/GltfReader.h>
#include <CesiumUtility/CreditSystem.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCountedNonThreadSafe.h>
#include <CesiumUtility/ReferenceCounted.h>
#include <CesiumUtility/Tracing.h>

#include <spdlog/fwd.h>
Expand Down
131 changes: 131 additions & 0 deletions CesiumUtility/include/CesiumUtility/ReferenceCounted.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#pragma once

#include <cstdint>

#ifndef NDEBUG
#include <thread>
#endif

namespace CesiumUtility {

#ifndef NDEBUG
template <bool isThreadSafe> class ThreadIdHolder;

template <> class ThreadIdHolder<false> {
ThreadIdHolder() : _threadID(std::this_thread::get_id()) {}

std::thread::id _threadID;

template <typename T, bool isThreadSafe> friend class ReferenceCounted;
};

template <> class ThreadIdHolder<true> {};
#endif

/**
* @brief A reference-counted base class, meant to be used with
* {@link IntrusivePointer}.
*
* Consider using {@link ReferenceCountedThreadSafe} or
* {@link ReferenceCountedNoThreadSafe} instead of using this class directly.
*
* @tparam T The type that is _deriving_ from this class. For example, you
* should declare your class as
* `class MyClass : public ReferenceCounted<MyClass> { ... };`
* @tparam isThreadSafe If `true`, the reference count will be thread-safe by
* using `std::atomic`, allowing references to safely be added and removed from
* any thread at any time. The object will be destroyed in the thread that
* releases the last reference. If false, it uses a simple integer for the
* reference count, which is not thread safe. In this case, references must be
* added and removed (including automatically via `IntrusivePointer`) from only
* one thread at a time. However, this mode has a bit less overhead for objects
* that are only ever accessed from a single thread.
*/
template <typename T, bool isThreadSafe = true>
class ReferenceCounted
#ifndef NDEBUG
: public ThreadIdHolder<isThreadSafe>
#endif
{
public:
ReferenceCounted() noexcept {}
~ReferenceCounted() noexcept { assert(this->_referenceCount == 0); }

/**
* @brief Adds a counted reference to this object. Use
* {@link CesiumUtility::IntrusivePointer} instead of calling this method
* directly.
*/
void addReference() const /*noexcept*/ {
#ifndef NDEBUG
if constexpr (!isThreadSafe) {
assert(std::this_thread::get_id() == this->_threadID);
}
#endif

++this->_referenceCount;
}

/**
* @brief Removes a counted reference from this object. When the last
* reference is removed, this method will delete this instance. Use
* {@link CesiumUtility::IntrusivePointer} instead of calling this method
* directly.
*/
void releaseReference() const /*noexcept*/ {
#ifndef NDEBUG
if constexpr (!isThreadSafe) {
assert(std::this_thread::get_id() == this->_threadID);
}
#endif

assert(this->_referenceCount > 0);
const int32_t references = --this->_referenceCount;
if (references == 0) {
delete static_cast<const T*>(this);
}
}

/**
* @brief Returns the current reference count of this instance.
*/
std::int32_t getReferenceCount() const noexcept {
return this->_referenceCount;
}

private:
using ThreadSafeCounter = std::atomic<std::int32_t>;
using NonThreadSafeCounter = std::int32_t;
using CounterType =
std::conditional_t<isThreadSafe, ThreadSafeCounter, NonThreadSafeCounter>;

mutable CounterType _referenceCount{0};
};

/**
* @brief A reference-counted base class, meant to be used with
* {@link IntrusivePointer}. The reference count is thread-safe, so references
* may be added and removed from any thread at any time. The object will be
* destroyed in the thread that releases the last reference.
*
* @tparam T The type that is _deriving_ from this class. For example, you
* should declare your class as
* `class MyClass : public ReferenceCountedThreadSafe<MyClass> { ... };`
*/
template <typename T>
using ReferenceCountedThreadSafe = ReferenceCounted<T, true>;

/**
* @brief A reference-counted base class, meant to be used with
* {@link IntrusivePointer}. The reference count is not thread-safe, so
* references must be added and removed (including automatically via
* `IntrusivePointer`) from only one thread at a time.
*
* @tparam T The type that is _deriving_ from this class. For example, you
* should declare your class as
* `class MyClass : public ReferenceCountedNonThreadSafe<MyClass> { ... };`
*/
template <typename T>
using ReferenceCountedNonThreadSafe = ReferenceCounted<T, false>;

} // namespace CesiumUtility

This file was deleted.

0 comments on commit e443a65

Please sign in to comment.