Rocket.Chat.ReactNative/ios/Pods/Folly/folly/CancellationToken-inl.h

352 lines
10 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
#include <algorithm>
#include <cstdint>
#include <limits>
#include <utility>
#include <glog/logging.h>
namespace folly {
namespace detail {
// Internal cancellation state object.
class CancellationState {
public:
FOLLY_NODISCARD static CancellationStateSourcePtr create();
private:
// Constructed initially with a CancellationSource reference count of 1.
CancellationState() noexcept;
~CancellationState();
friend struct CancellationStateTokenDeleter;
friend struct CancellationStateSourceDeleter;
void removeTokenReference() noexcept;
void removeSourceReference() noexcept;
public:
FOLLY_NODISCARD CancellationStateTokenPtr addTokenReference() noexcept;
FOLLY_NODISCARD CancellationStateSourcePtr addSourceReference() noexcept;
bool tryAddCallback(
CancellationCallback* callback,
bool incrementRefCountIfSuccessful) noexcept;
void removeCallback(CancellationCallback* callback) noexcept;
bool isCancellationRequested() const noexcept;
bool canBeCancelled() const noexcept;
// Request cancellation.
// Return 'true' if cancellation had already been requested.
// Return 'false' if this was the first thread to request
// cancellation.
bool requestCancellation() noexcept;
private:
void lock() noexcept;
void unlock() noexcept;
void unlockAndIncrementTokenCount() noexcept;
void unlockAndDecrementTokenCount() noexcept;
bool tryLockAndCancelUnlessCancelled() noexcept;
template <typename Predicate>
bool tryLock(Predicate predicate) noexcept;
static bool canBeCancelled(std::uint64_t state) noexcept;
static bool isCancellationRequested(std::uint64_t state) noexcept;
static bool isLocked(std::uint64_t state) noexcept;
static constexpr std::uint64_t kCancellationRequestedFlag = 1;
static constexpr std::uint64_t kLockedFlag = 2;
static constexpr std::uint64_t kTokenReferenceCountIncrement = 4;
static constexpr std::uint64_t kSourceReferenceCountIncrement =
std::uint64_t(1) << 33u;
static constexpr std::uint64_t kTokenReferenceCountMask =
(kSourceReferenceCountIncrement - 1u) -
(kTokenReferenceCountIncrement - 1u);
static constexpr std::uint64_t kSourceReferenceCountMask =
std::numeric_limits<std::uint64_t>::max() -
(kSourceReferenceCountIncrement - 1u);
// Bit 0 - Cancellation Requested
// Bit 1 - Locked Flag
// Bits 2-32 - Token reference count (max ~2 billion)
// Bits 33-63 - Source reference count (max ~2 billion)
std::atomic<std::uint64_t> state_;
CancellationCallback* head_;
std::thread::id signallingThreadId_;
};
inline void CancellationStateTokenDeleter::operator()(
CancellationState* state) noexcept {
state->removeTokenReference();
}
inline void CancellationStateSourceDeleter::operator()(
CancellationState* state) noexcept {
state->removeSourceReference();
}
} // namespace detail
inline CancellationToken::CancellationToken(
const CancellationToken& other) noexcept
: state_() {
if (other.state_) {
state_ = other.state_->addTokenReference();
}
}
inline CancellationToken::CancellationToken(CancellationToken&& other) noexcept
: state_(std::move(other.state_)) {}
inline CancellationToken& CancellationToken::operator=(
const CancellationToken& other) noexcept {
if (state_ != other.state_) {
CancellationToken temp{other};
swap(temp);
}
return *this;
}
inline CancellationToken& CancellationToken::operator=(
CancellationToken&& other) noexcept {
state_ = std::move(other.state_);
return *this;
}
inline bool CancellationToken::isCancellationRequested() const noexcept {
return state_ != nullptr && state_->isCancellationRequested();
}
inline bool CancellationToken::canBeCancelled() const noexcept {
return state_ != nullptr && state_->canBeCancelled();
}
inline void CancellationToken::swap(CancellationToken& other) noexcept {
std::swap(state_, other.state_);
}
inline CancellationToken::CancellationToken(
detail::CancellationStateTokenPtr state) noexcept
: state_(std::move(state)) {}
inline bool operator==(
const CancellationToken& a,
const CancellationToken& b) noexcept {
return a.state_ == b.state_;
}
inline bool operator!=(
const CancellationToken& a,
const CancellationToken& b) noexcept {
return !(a == b);
}
inline CancellationSource::CancellationSource()
: state_(detail::CancellationState::create()) {}
inline CancellationSource::CancellationSource(
const CancellationSource& other) noexcept
: state_() {
if (other.state_) {
state_ = other.state_->addSourceReference();
}
}
inline CancellationSource::CancellationSource(
CancellationSource&& other) noexcept
: state_(std::move(other.state_)) {}
inline CancellationSource& CancellationSource::operator=(
const CancellationSource& other) noexcept {
if (state_ != other.state_) {
CancellationSource temp{other};
swap(temp);
}
return *this;
}
inline CancellationSource& CancellationSource::operator=(
CancellationSource&& other) noexcept {
state_ = std::move(other.state_);
return *this;
}
inline CancellationSource CancellationSource::invalid() noexcept {
return CancellationSource{detail::CancellationStateSourcePtr{}};
}
inline bool CancellationSource::isCancellationRequested() const noexcept {
return state_ != nullptr && state_->isCancellationRequested();
}
inline bool CancellationSource::canBeCancelled() const noexcept {
return state_ != nullptr;
}
inline CancellationToken CancellationSource::getToken() const noexcept {
if (state_ != nullptr) {
return CancellationToken{state_->addTokenReference()};
}
return CancellationToken{};
}
inline bool CancellationSource::requestCancellation() const noexcept {
if (state_ != nullptr) {
return state_->requestCancellation();
}
return false;
}
inline void CancellationSource::swap(CancellationSource& other) noexcept {
std::swap(state_, other.state_);
}
inline CancellationSource::CancellationSource(
detail::CancellationStateSourcePtr&& state) noexcept
: state_(std::move(state)) {}
template <
typename Callable,
std::enable_if_t<
std::is_constructible<CancellationCallback::VoidFunction, Callable>::
value,
int>>
inline CancellationCallback::CancellationCallback(
CancellationToken&& ct,
Callable&& callable)
: next_(nullptr),
prevNext_(nullptr),
state_(nullptr),
callback_(static_cast<Callable&&>(callable)),
destructorHasRunInsideCallback_(nullptr),
callbackCompleted_(false) {
if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, false)) {
state_ = ct.state_.release();
}
}
template <
typename Callable,
std::enable_if_t<
std::is_constructible<CancellationCallback::VoidFunction, Callable>::
value,
int>>
inline CancellationCallback::CancellationCallback(
const CancellationToken& ct,
Callable&& callable)
: next_(nullptr),
prevNext_(nullptr),
state_(nullptr),
callback_(static_cast<Callable&&>(callable)),
destructorHasRunInsideCallback_(nullptr),
callbackCompleted_(false) {
if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, true)) {
state_ = ct.state_.get();
}
}
inline CancellationCallback::~CancellationCallback() {
if (state_ != nullptr) {
state_->removeCallback(this);
}
}
inline void CancellationCallback::invokeCallback() noexcept {
// Invoke within a noexcept context so that we std::terminate() if it throws.
callback_();
}
namespace detail {
inline CancellationStateSourcePtr CancellationState::create() {
return CancellationStateSourcePtr{new CancellationState()};
}
inline CancellationState::CancellationState() noexcept
: state_(kSourceReferenceCountIncrement),
head_(nullptr),
signallingThreadId_() {}
inline CancellationStateTokenPtr
CancellationState::addTokenReference() noexcept {
state_.fetch_add(kTokenReferenceCountIncrement, std::memory_order_relaxed);
return CancellationStateTokenPtr{this};
}
inline void CancellationState::removeTokenReference() noexcept {
const auto oldState = state_.fetch_sub(
kTokenReferenceCountIncrement, std::memory_order_acq_rel);
DCHECK(
(oldState & kTokenReferenceCountMask) >= kTokenReferenceCountIncrement);
if (oldState < (2 * kTokenReferenceCountIncrement)) {
delete this;
}
}
inline CancellationStateSourcePtr
CancellationState::addSourceReference() noexcept {
state_.fetch_add(kSourceReferenceCountIncrement, std::memory_order_relaxed);
return CancellationStateSourcePtr{this};
}
inline void CancellationState::removeSourceReference() noexcept {
const auto oldState = state_.fetch_sub(
kSourceReferenceCountIncrement, std::memory_order_acq_rel);
DCHECK(
(oldState & kSourceReferenceCountMask) >= kSourceReferenceCountIncrement);
if (oldState <
(kSourceReferenceCountIncrement + kTokenReferenceCountIncrement)) {
delete this;
}
}
inline bool CancellationState::isCancellationRequested() const noexcept {
return isCancellationRequested(state_.load(std::memory_order_acquire));
}
inline bool CancellationState::canBeCancelled() const noexcept {
return canBeCancelled(state_.load(std::memory_order_acquire));
}
inline bool CancellationState::canBeCancelled(std::uint64_t state) noexcept {
// Can be cancelled if there is at least one CancellationSource ref-count
// or if cancellation has been requested.
return (state >= kSourceReferenceCountIncrement) ||
isCancellationRequested(state);
}
inline bool CancellationState::isCancellationRequested(
std::uint64_t state) noexcept {
return (state & kCancellationRequestedFlag) != 0;
}
inline bool CancellationState::isLocked(std::uint64_t state) noexcept {
return (state & kLockedFlag) != 0;
}
} // namespace detail
} // namespace folly