// 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. #pragma once #include #include #include #include "yarpl/Refcounted.h" #include "yarpl/single/SingleSubscription.h" namespace yarpl { namespace single { /** * Implementation that allows checking if a Subscription is cancelled. */ class AtomicBoolSingleSubscription : public SingleSubscription { public: void cancel() override { cancelled_ = true; } bool isCancelled() const { return cancelled_; } private: std::atomic_bool cancelled_{false}; }; /** * Implementation that gets a callback when cancellation occurs. */ class CallbackSingleSubscription : public SingleSubscription { public: explicit CallbackSingleSubscription(std::function onCancel) : onCancel_(std::move(onCancel)) {} void cancel() override { bool expected = false; // mark cancelled 'true' and only if successful invoke 'onCancel()' if (cancelled_.compare_exchange_strong(expected, true)) { onCancel_(); } } bool isCancelled() const { return cancelled_; } private: std::atomic_bool cancelled_{false}; std::function onCancel_; }; /** * Implementation that can be cancelled with or without * a delegate, and when the delegate exists (before or after cancel) * it will be cancelled in a thread-safe manner. */ class DelegateSingleSubscription : public SingleSubscription { public: explicit DelegateSingleSubscription() {} void cancel() override { bool shouldCancelDelegate = false; { std::lock_guard g(m_); cancelled_ = true; if (delegate_) { shouldCancelDelegate = true; } } // cancel without holding lock if (shouldCancelDelegate) { delegate_->cancel(); } } bool isCancelled() const { std::lock_guard g(m_); return cancelled_; } /** * This can be called once. */ void setDelegate(std::shared_ptr d) { bool shouldCancelDelegate = false; { std::lock_guard g(m_); if (delegate_) { throw std::runtime_error("Delegate already set. Only one permitted."); } delegate_ = std::move(d); if (cancelled_) { shouldCancelDelegate = true; } } // cancel without holding lock if (shouldCancelDelegate) { delegate_->cancel(); } } private: // all must be protected by a mutex mutable std::mutex m_; bool cancelled_{false}; std::shared_ptr delegate_; }; class SingleSubscriptions { public: static std::shared_ptr create( std::function onCancel) { return std::make_shared(std::move(onCancel)); } static std::shared_ptr create( std::atomic_bool& cancelled) { return create([&cancelled]() { cancelled = true; }); } static std::shared_ptr empty() { return std::make_shared(); } static std::shared_ptr atomicBoolSubscription() { return std::make_shared(); } }; } // namespace single } // namespace yarpl