| // Copyright 2026 Google LLC |
| // |
| // 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 |
| // |
| // https://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. |
| // |
| |
| #ifndef THIRD_PARTY_CREDENTIO_ASYNC_CANCELLATION_TOKEN_H_ |
| #define THIRD_PARTY_CREDENTIO_ASYNC_CANCELLATION_TOKEN_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <utility> |
| |
| #include "absl/base/thread_annotations.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/synchronization/mutex.h" |
| |
| namespace credentio { |
| |
| class CancellationToken; |
| |
| // Internal shared state for cancellation. |
| class CancellationState { |
| public: |
| CancellationState() = default; |
| |
| bool IsCancelled() const { |
| absl::MutexLock lock(mutex_); |
| return cancelled_; |
| } |
| |
| void Cancel(); |
| |
| // Returns >0 registration ID if successfully registered. |
| // Returns 0 if already cancelled (and executes callback immediately inline). |
| uint64_t RegisterCallback(absl::AnyInvocable<void()> cb); |
| |
| void DeregisterCallback(uint64_t id); |
| |
| private: |
| mutable absl::Mutex mutex_; |
| bool cancelled_ ABSL_GUARDED_BY(mutex_) = false; |
| uint64_t next_id_ ABSL_GUARDED_BY(mutex_) = 1; |
| absl::flat_hash_map<uint64_t, absl::AnyInvocable<void()>> callbacks_ |
| ABSL_GUARDED_BY(mutex_); |
| }; |
| |
| // Represents a read-only view of a cancellation state. |
| class CancellationToken { |
| public: |
| CancellationToken() = default; |
| |
| bool IsCancelled() const { |
| if (!state_) return false; |
| return state_->IsCancelled(); |
| } |
| |
| // Copyable and movable. |
| CancellationToken(const CancellationToken&) = default; |
| CancellationToken& operator=(const CancellationToken&) = default; |
| CancellationToken(CancellationToken&&) = default; |
| CancellationToken& operator=(CancellationToken&&) = default; |
| |
| private: |
| friend class CancellationSource; |
| friend class CancellationCallback; |
| |
| explicit CancellationToken(std::shared_ptr<CancellationState> state) |
| : state_(std::move(state)) {} |
| |
| std::shared_ptr<CancellationState> state_; |
| }; |
| |
| // The source of cancellation. |
| class CancellationSource { |
| public: |
| CancellationSource() : state_(std::make_shared<CancellationState>()) {} |
| |
| void Cancel() { |
| if (state_) { |
| state_->Cancel(); |
| } |
| } |
| |
| CancellationToken GetToken() const { return CancellationToken(state_); } |
| |
| private: |
| std::shared_ptr<CancellationState> state_; |
| }; |
| |
| // Represents a registration of a callback against a CancellationToken. |
| // When this object falls out of scope, the callback is automatically |
| // deregistered. |
| class CancellationCallback { |
| public: |
| CancellationCallback(CancellationToken token, absl::AnyInvocable<void()> cb) |
| : state_(std::move(token.state_)) { |
| if (state_) { |
| id_ = state_->RegisterCallback(std::move(cb)); |
| } |
| } |
| |
| ~CancellationCallback() { |
| if (state_ && id_ != 0) { |
| state_->DeregisterCallback(id_); |
| } |
| } |
| |
| // Non-copyable, non-movable. |
| CancellationCallback(const CancellationCallback&) = delete; |
| CancellationCallback& operator=(const CancellationCallback&) = delete; |
| |
| private: |
| std::shared_ptr<CancellationState> state_; |
| uint64_t id_ = 0; |
| }; |
| |
| } // namespace credentio |
| |
| #endif // THIRD_PARTY_CREDENTIO_ASYNC_CANCELLATION_TOKEN_H_ |