| /* |
| * Copyright 2015 Nest Labs, Inc. |
| * |
| * 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 <cassert> |
| #include <memory> |
| #include <mutex> |
| #include <condition_variable> |
| |
| #ifdef __GNUC__ |
| #define FOLLY_CANCELLATION_WARN_UNUSED __attribute__((warn_unused_result)) |
| #else |
| #define FOLLY_CANCELLATION_WARN_UNUSED |
| #endif |
| |
| namespace folly { |
| |
| class Cancellation; |
| |
| namespace detail { |
| |
| /** |
| * The shared state object that implements the Cancellation object |
| * |
| * You must not use this class directly. |
| * |
| * This object is the <em>actual</em> Cancellation. It implements the |
| * std::BasicLockable interface, and therefore may be controlled by |
| * objects like std::lock_guard<T>. |
| */ |
| struct CancellationSharedState |
| { |
| public: |
| /* Cancellation shall *only* use the 'protected' or public methods. |
| */ |
| friend class folly::Cancellation; |
| |
| CancellationSharedState() {} |
| ~CancellationSharedState() {} |
| |
| /* non-copyable */ |
| CancellationSharedState(const CancellationSharedState& o) = delete; |
| CancellationSharedState& operator=(const CancellationSharedState& o) = delete; |
| |
| /* non-moveable (thread safe move?) */ |
| CancellationSharedState(CancellationSharedState&& o) = delete; |
| CancellationSharedState& operator=(CancellationSharedState&& o) = delete; |
| |
| /* std::BasicLockable concept. Locks state. */ |
| void lock() |
| { |
| bool have_lock = try_lock(); |
| if (!have_lock) { |
| throw std::logic_error("Cancellation is cancelled, can't lock"); |
| } |
| } |
| |
| /* std::BasicLockable concept. Unocks state. */ |
| void unlock() |
| { |
| std::lock_guard<std::mutex> lock(mutex_); |
| assert(hold_state_refs_ > 0); |
| --hold_state_refs_; |
| if (hold_state_refs_ == 0) { |
| cond_.notify_all(); |
| } |
| } |
| |
| /* std::Lockable concept. Attempts to lock state. Returns true if NOT cancelled. */ |
| bool try_lock() |
| { |
| std::lock_guard<std::mutex> lock(mutex_); |
| if (is_cancelled_) { |
| return false; |
| } |
| ++hold_state_refs_; |
| assert(hold_state_refs_ != 0); /* overflow */ |
| return true; |
| } |
| |
| protected: |
| |
| bool is_cancelled() const |
| { |
| std::lock_guard<std::mutex> lock(mutex_); |
| return is_cancelled_; |
| } |
| |
| /* Set state to CANCELLED. Blocks if there are active state refs. */ |
| void cancel() |
| { |
| // TODO: this implementation is subject to a live-lock |
| std::unique_lock<std::mutex> lock(mutex_); |
| ++cancel_refs_; |
| assert(cancel_refs_ != 0); /* overflow */ |
| while (hold_state_refs_ > 0) { |
| cond_.wait(lock); |
| } |
| is_cancelled_ = true; |
| assert(cancel_refs_ > 0); |
| --cancel_refs_; |
| } |
| |
| private: |
| mutable std::mutex mutex_; |
| mutable std::condition_variable cond_; |
| unsigned cancel_refs_ {0}; //< number of cancel() calls currently active |
| unsigned hold_state_refs_ {0}; //< number of state refs currently active |
| bool is_cancelled_ {false}; //< state. false = not cancelled, true = cancelled. |
| }; |
| |
| } /* namespace detail */ |
| } /* namespace folly */ |