Skip to content
Merged
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
55 changes: 48 additions & 7 deletions include/tmc/channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1917,7 +1917,16 @@ template <typename T, typename Config> class chan_tok {
chan_tok(std::shared_ptr<chan_t>&& Chan) noexcept
: chan{std::move(Chan)}, haz_ptr{nullptr} {}

void assert_valid() const noexcept {
assert(
chan != nullptr &&
"This chan_tok is empty (default-constructed or moved-from). Assign a "
"valid token to it before use."
);
}

hazard_ptr* get_hazard_ptr() noexcept {
assert_valid();
if (haz_ptr == nullptr) [[unlikely]] {
haz_ptr = chan->get_hazard_ptr();
}
Expand Down Expand Up @@ -2134,7 +2143,10 @@ template <typename T, typename Config> class chan_tok {
///
/// This function is idempotent and thread-safe. It is not lock-free. It may
/// contend the lock against `close()` and `drain()`.
void close() noexcept { chan->close(); }
void close() noexcept {
assert_valid();
chan->close();
}

/// If the channel is not already closed, it will be closed as if by calling
/// close(). Then, waits for consumers to drain all remaining data from the
Expand All @@ -2148,7 +2160,10 @@ template <typename T, typename Config> class chan_tok {
///
/// This function is idempotent and thread-safe. It is not lock-free. It may
/// contend the lock against `close()` and `drain()`.
tmc::task<void> drain() noexcept { return chan->drain(); }
tmc::task<void> drain() noexcept {
assert_valid();
return chan->drain();
}

/// If the channel is not already closed, it will be closed as if by calling
/// close(). Then, waits for consumers to drain all remaining data from the
Expand All @@ -2167,14 +2182,18 @@ template <typename T, typename Config> class chan_tok {
///
/// This function is idempotent and thread-safe. It is not lock-free. It may
/// contend the lock against `close()` and `drain()`.
void drain_wait() noexcept { chan->drain_wait(); }
void drain_wait() noexcept {
assert_valid();
chan->drain_wait();
}

/// If true, spent blocks will be cleared and moved to the tail of the queue.
/// If false, spent blocks will be deleted.
/// Default: true
///
/// If Config::EmbedFirstBlock == true, this will be forced to true.
chan_tok& set_reuse_blocks(bool Reuse) noexcept TMC_LIFETIMEBOUND {
assert_valid();
if constexpr (!Config::EmbedFirstBlock) {
chan->ReuseBlocks.store(Reuse, std::memory_order_relaxed);
}
Expand All @@ -2185,6 +2204,7 @@ template <typename T, typename Config> class chan_tok {
/// many times. Each spin wait is an asm("pause") and reload.
/// Default: 0
chan_tok& set_consumer_spins(size_t SpinCount) noexcept TMC_LIFETIMEBOUND {
assert_valid();
chan->ConsumerSpins.store(SpinCount, std::memory_order_relaxed);
return *this;
}
Expand All @@ -2195,12 +2215,31 @@ template <typename T, typename Config> class chan_tok {
/// value of 2,000,000 represents an item being pushed every 500ns. This
/// behavior can be disabled entirely by setting this to 0.
chan_tok& set_heavy_load_threshold(size_t Threshold) noexcept TMC_LIFETIMEBOUND {
assert_valid();
size_t cycles =
Threshold == 0 ? 0 : TMC_CPU_FREQ * chan_t::ClusterPeriod / Threshold;
chan->MinClusterCycles.store(cycles, std::memory_order_relaxed);
return *this;
}

/// Default Constructor: Creates an empty token that is not associated with
/// any channel. (A token also becomes empty after being moved-from.)
///
/// The only valid operations on an empty token are to assign another token
/// to it, copy or move from it, call `valid()`, or destroy it. Calling any
/// other member function on an empty token will assert in debug builds, and
/// is undefined behavior in release builds.
///
/// To associate this token with a channel, assign to it from a valid token
/// (produced by `make_channel()` or by copying another valid token).
chan_tok() noexcept : haz_ptr{nullptr} {}

/// Returns true if this token is associated with a channel.
///
/// Returns false if this token is empty (default-constructed or moved-from).
/// An empty token can be made valid by assigning a valid token to it.
bool valid() const noexcept { return chan != nullptr; }

/// Copy Constructor: The new chan_tok will have its own hazard pointer so
/// that it can be used concurrently with the other token.
///
Expand All @@ -2225,15 +2264,17 @@ template <typename T, typename Config> class chan_tok {
/// channel.
chan_tok new_token() noexcept { return chan_tok(*this); }

/// Move Constructor: The moved-from token will become empty; it will release
/// its channel pointer, and its hazard pointer.
/// Move Constructor: The moved-from token will become empty (`valid()` will
/// return false); it will release its channel pointer, and its hazard
/// pointer.
chan_tok(chan_tok&& Other) noexcept
: chan(std::move(Other.chan)), haz_ptr{Other.haz_ptr} {
Other.haz_ptr = nullptr;
}

/// Move Assignment: The moved-from token will become empty; it will release
/// its channel pointer, and its hazard pointer.
/// Move Assignment: The moved-from token will become empty (`valid()` will
/// return false); it will release its channel pointer, and its hazard
/// pointer.
///
/// If the other token is from a different channel, this token will now point
/// to that channel.
Expand Down
Loading