| // 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_ |