blob: 0f676aafadaab7abe72731b17d7db67c508912cb [file]
// 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
#ifndef __PROCESS_SHARED_HPP__
#define __PROCESS_SHARED_HPP__
#include <atomic>
#include <cstddef>
#include <memory>
#include <glog/logging.h>
#include <process/future.hpp>
namespace process {
// Forward declaration.
template <typename T>
class Owned;
// Represents a shared pointer and therefore enforces 'const' access.
template <typename T>
class Shared
{
public:
Shared();
explicit Shared(T* t);
/*implicit*/ Shared(std::nullptr_t) : Shared(static_cast<T*>(nullptr)) {}
bool operator==(const Shared<T>& that) const;
bool operator<(const Shared<T>& that) const;
// Enforces const access semantics.
const T& operator*() const;
const T* operator->() const;
const T* get() const;
bool unique() const;
void reset();
void reset(T* t);
void swap(Shared<T>& that);
// Transfers ownership of the pointer by waiting for exclusive
// access (i.e., no other Shared instances). This shared pointer
// will be reset after this function is invoked. If multiple shared
// pointers pointing to the same object all want to be upgraded,
// only one of them may succeed and the rest will get failures.
Future<Owned<T>> own();
private:
struct Data
{
explicit Data(T* _t);
~Data();
T* t;
std::atomic_bool owned;
Promise<Owned<T>> promise;
};
std::shared_ptr<Data> data;
};
template <typename T>
Shared<T>::Shared() {}
template <typename T>
Shared<T>::Shared(T* t)
{
if (t != nullptr) {
data.reset(new Data(t));
}
}
template <typename T>
bool Shared<T>::operator==(const Shared<T>& that) const
{
return data == that.data;
}
template <typename T>
bool Shared<T>::operator<(const Shared<T>& that) const
{
return data < that.data;
}
template <typename T>
const T& Shared<T>::operator*() const
{
return *CHECK_NOTNULL(get());
}
template <typename T>
const T* Shared<T>::operator->() const
{
return CHECK_NOTNULL(get());
}
template <typename T>
const T* Shared<T>::get() const
{
if (data == nullptr) {
return nullptr;
} else {
return data->t;
}
}
template <typename T>
bool Shared<T>::unique() const
{
return data.unique();
}
template <typename T>
void Shared<T>::reset()
{
data.reset();
}
template <typename T>
void Shared<T>::reset(T* t)
{
if (t == nullptr) {
data.reset();
} else {
data.reset(new Data(t));
}
}
template <typename T>
void Shared<T>::swap(Shared<T>& that)
{
data.swap(that.data);
}
template <typename T>
Future<Owned<T>> Shared<T>::own()
{
// If two threads simultaneously access this object and at least one
// of them is a write, the behavior is undefined. This is similar to
// boost::shared_ptr. For more details, please refer to the boost
// shared_ptr document (section "Thread Safety").
if (data == nullptr) {
return Owned<T>(nullptr);
}
bool false_value = false;
if (!data->owned.compare_exchange_strong(false_value, true)) {
return Failure("Ownership has already been transferred");
}
Future<Owned<T>> future = data->promise.future();
data.reset();
return future;
}
template <typename T>
Shared<T>::Data::Data(T* _t)
: t(CHECK_NOTNULL(_t)), owned(false) {}
template <typename T>
Shared<T>::Data::~Data()
{
if (owned.load()) {
promise.set(Owned<T>(t));
} else {
delete t;
}
}
} // namespace process {
#endif // __PROCESS_SHARED_HPP__