blob: 4507e9875ee931f106a94f1ac8a0bee7b355b6bb [file] [log] [blame]
/*
* 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 */