AimRT/install_x64/include/unifex/inplace_stop_token.hpp
2025-01-12 19:51:34 +08:00

297 lines
8.0 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <unifex/config.hpp>
#include <unifex/manual_lifetime.hpp>
#include <unifex/spin_wait.hpp>
#include <unifex/std_concepts.hpp>
#include <unifex/stop_token_concepts.hpp>
#include <unifex/type_index.hpp>
#include <atomic>
#include <thread>
#include <cstdint>
#include <type_traits>
#include <unifex/detail/prologue.hpp>
namespace unifex {
class inplace_stop_source;
class inplace_stop_token;
template <typename F>
class inplace_stop_callback;
class inplace_stop_callback_base {
public:
void execute() noexcept {
this->execute_(this);
}
#ifndef NDEBUG
char const* type_name() const noexcept {
return type_name_;
}
#endif
protected:
using execute_fn = void(inplace_stop_callback_base* cb) noexcept;
#ifndef NDEBUG
explicit inplace_stop_callback_base(inplace_stop_source* source, execute_fn* execute, char const* type_name) noexcept
: source_(source), execute_(execute), type_name_(type_name) {}
#else
explicit inplace_stop_callback_base(inplace_stop_source* source, execute_fn* execute) noexcept
: source_(source), execute_(execute) {}
#endif
void register_callback() noexcept;
friend inplace_stop_source;
inplace_stop_source* source_;
execute_fn* execute_;
inplace_stop_callback_base* next_ = nullptr;
inplace_stop_callback_base** prevPtr_ = nullptr;
bool* removedDuringCallback_ = nullptr;
std::atomic<bool> callbackCompleted_{false};
#ifndef NDEBUG
char const* type_name_ = nullptr;
#endif
};
class inplace_stop_source {
public:
inplace_stop_source() noexcept = default;
~inplace_stop_source();
inplace_stop_source(const inplace_stop_source&) = delete;
inplace_stop_source(inplace_stop_source&&) = delete;
inplace_stop_source& operator=(inplace_stop_source&&) = delete;
inplace_stop_source& operator=(const inplace_stop_source&) = delete;
bool request_stop() noexcept;
inplace_stop_token get_token() noexcept;
bool stop_requested() const noexcept {
return (state_.load(std::memory_order_acquire) & stop_requested_flag) != 0;
}
private:
friend inplace_stop_token;
friend inplace_stop_callback_base;
template <typename F>
friend class inplace_stop_callback;
std::uint8_t lock() noexcept;
void unlock(std::uint8_t oldState) noexcept;
bool try_lock_unless_stop_requested(bool setStopRequested) noexcept;
bool try_add_callback(inplace_stop_callback_base* callback) noexcept;
void remove_callback(inplace_stop_callback_base* callback) noexcept;
static constexpr std::uint8_t stop_requested_flag = 1;
static constexpr std::uint8_t locked_flag = 2;
std::atomic<std::uint8_t> state_{0};
inplace_stop_callback_base* callbacks_ = nullptr;
std::thread::id notifyingThreadId_;
};
class inplace_stop_token {
public:
template <typename F>
using callback_type = inplace_stop_callback<F>;
inplace_stop_token() noexcept : source_(nullptr) {}
inplace_stop_token(const inplace_stop_token& other) noexcept = default;
inplace_stop_token(inplace_stop_token&& other) noexcept
: source_(std::exchange(other.source_, {})) {}
inplace_stop_token& operator=(const inplace_stop_token& other) noexcept = default;
inplace_stop_token& operator=(inplace_stop_token&& other) noexcept {
source_ = std::exchange(other.source_, nullptr);
return *this;
}
bool stop_requested() const noexcept {
return source_ != nullptr && source_->stop_requested();
}
bool stop_possible() const noexcept {
return source_ != nullptr;
}
void swap(inplace_stop_token& other) noexcept {
std::swap(source_, other.source_);
}
friend bool operator==(const inplace_stop_token& a, const inplace_stop_token& b) noexcept {
return a.source_ == b.source_;
}
friend bool operator!=(const inplace_stop_token& a, const inplace_stop_token& b) noexcept {
return !(a == b);
}
private:
friend inplace_stop_source;
template <typename F>
friend class inplace_stop_callback;
explicit inplace_stop_token(inplace_stop_source* source) noexcept
: source_(source) {}
inplace_stop_source* source_;
};
inline inplace_stop_token inplace_stop_source::get_token() noexcept {
return inplace_stop_token{this};
}
template <typename F>
class inplace_stop_callback final : private inplace_stop_callback_base {
public:
template(typename T = F)
(requires convertible_to<T, F>)
explicit inplace_stop_callback(inplace_stop_token token, T&& func) noexcept(
std::is_nothrow_constructible_v<F, T>)
#ifndef NDEBUG
: inplace_stop_callback_base(token.source_, &inplace_stop_callback::execute_impl, unifex::type_id<F>().name())
#else
: inplace_stop_callback_base(token.source_, &inplace_stop_callback::execute_impl)
#endif
, func_((T&&) func) {
this->register_callback();
}
~inplace_stop_callback() {
if (source_ != nullptr) {
source_->remove_callback(this);
}
}
private:
static void execute_impl(inplace_stop_callback_base* cb) noexcept {
auto& self = *static_cast<inplace_stop_callback*>(cb);
self.func_();
}
UNIFEX_NO_UNIQUE_ADDRESS F func_;
};
inline void inplace_stop_callback_base::register_callback() noexcept {
if (source_ != nullptr) {
if (!source_->try_add_callback(this)) {
source_ = nullptr;
// Callback not registered because stop_requested() was true.
// Execute inline here.
execute();
}
}
}
namespace detail {
struct forward_stop_request_to_inplace_stop_source {
inplace_stop_source& source;
forward_stop_request_to_inplace_stop_source(inplace_stop_source& s) noexcept
: source(s) {}
void operator()() const noexcept {
source.request_stop();
}
};
} // namespace detail
// Helper class for adapting an incoming StopToken type to an
// inplace_stop_token.
template<typename StopToken, typename = void>
class inplace_stop_token_adapter {
public:
inplace_stop_token subscribe(StopToken stoken) noexcept {
const bool stopPossible = stoken.stop_possible();
callback_.construct(std::move(stoken), source_);
return stopPossible ? source_.get_token() : inplace_stop_token{};
}
void unsubscribe() noexcept {
callback_.destruct();
}
private:
using stop_callback = typename StopToken::template callback_type<detail::forward_stop_request_to_inplace_stop_source>;
inplace_stop_source source_;
UNIFEX_NO_UNIQUE_ADDRESS manual_lifetime<stop_callback> callback_;
};
template<>
class inplace_stop_token_adapter<inplace_stop_token, void> {
public:
inplace_stop_token subscribe(inplace_stop_token stoken) noexcept {
return stoken;
}
void unsubscribe() noexcept {}
};
template<typename StopToken>
class inplace_stop_token_adapter<StopToken, std::enable_if_t<is_stop_never_possible_v<StopToken>>> {
public:
inplace_stop_token subscribe(StopToken) noexcept {
return inplace_stop_token{};
}
void unsubscribe() noexcept {}
};
/// \cond
namespace detail {
template <typename StopToken>
struct inplace_stop_token_adapter_subscription {
inplace_stop_token subscribe(StopToken stoken) noexcept {
isSubscribed_ = true;
return stopTokenAdapter_.subscribe(std::move(stoken));
}
void unsubscribe() noexcept {
if (isSubscribed_) {
isSubscribed_ = false;
stopTokenAdapter_.unsubscribe();
}
}
~inplace_stop_token_adapter_subscription() {
unsubscribe();
}
private:
bool isSubscribed_ = false;
UNIFEX_NO_UNIQUE_ADDRESS
inplace_stop_token_adapter<StopToken> stopTokenAdapter_{};
};
} // namespace detail
/// \endcond
} // namespace unifex
#include <unifex/detail/epilogue.hpp>