You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
7.0 KiB
218 lines
7.0 KiB
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef UTIL_WEAK_PTR_H_
|
|
#define UTIL_WEAK_PTR_H_
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "util/osp_logging.h"
|
|
|
|
namespace openscreen {
|
|
|
|
// Weak pointers are pointers to an object that do not affect its lifetime,
|
|
// and which may be invalidated (i.e. reset to nullptr) by the object, or its
|
|
// owner, at any time; most commonly when the object is about to be deleted.
|
|
//
|
|
// Weak pointers are useful when an object needs to be accessed safely by one
|
|
// or more objects other than its owner, and those callers can cope with the
|
|
// object vanishing and e.g. tasks posted to it being silently dropped.
|
|
// Reference-counting such an object would complicate the ownership graph and
|
|
// make it harder to reason about the object's lifetime.
|
|
//
|
|
// EXAMPLE:
|
|
//
|
|
// class Controller {
|
|
// public:
|
|
// void SpawnWorker() { new Worker(weak_factory_.GetWeakPtr()); }
|
|
// void WorkComplete(const Result& result) { ... }
|
|
// private:
|
|
// // Member variables should appear before the WeakPtrFactory, to ensure
|
|
// // that any WeakPtrs to Controller are invalidated before its members
|
|
// // variable's destructors are executed, rendering them invalid.
|
|
// WeakPtrFactory<Controller> weak_factory_{this};
|
|
// };
|
|
//
|
|
// class Worker {
|
|
// public:
|
|
// explicit Worker(WeakPtr<Controller> controller)
|
|
// : controller_(std::move(controller)) {}
|
|
// private:
|
|
// void DidCompleteAsynchronousProcessing(const Result& result) {
|
|
// if (controller_)
|
|
// controller_->WorkComplete(result);
|
|
// delete this;
|
|
// }
|
|
// const WeakPtr<Controller> controller_;
|
|
// };
|
|
//
|
|
// With this implementation a caller may use SpawnWorker() to dispatch multiple
|
|
// Workers and subsequently delete the Controller, without waiting for all
|
|
// Workers to have completed.
|
|
//
|
|
// ------------------------- IMPORTANT: Thread-safety -------------------------
|
|
//
|
|
// Generally, Open Screen code is meant to be single-threaded. For the few
|
|
// exceptional cases, the following is relevant:
|
|
//
|
|
// WeakPtrs may be created from WeakPtrFactory, and also duplicated/moved on any
|
|
// thread/sequence. However, they may only be dereferenced on the same
|
|
// thread/sequence that will ultimately execute the WeakPtrFactory destructor or
|
|
// call InvalidateWeakPtrs(). Otherwise, use-during-free or use-after-free is
|
|
// possible.
|
|
//
|
|
// openscreen::WeakPtr and WeakPtrFactory are similar, but not identical, to
|
|
// Chromium's base::WeakPtrFactory. Open Screen WeakPtrs may be safely created
|
|
// from WeakPtrFactory on any thread/sequence, since they are backed by the
|
|
// thread-safe bookkeeping of std::shared_ptr<>.
|
|
|
|
template <typename T>
|
|
class WeakPtrFactory;
|
|
|
|
template <typename T>
|
|
class WeakPtr {
|
|
public:
|
|
WeakPtr() = default;
|
|
~WeakPtr() = default;
|
|
|
|
// Copy/Move constructors and assignment operators.
|
|
WeakPtr(const WeakPtr& other) : impl_(other.impl_) {}
|
|
|
|
WeakPtr(WeakPtr&& other) noexcept : impl_(std::move(other.impl_)) {}
|
|
|
|
WeakPtr& operator=(const WeakPtr& other) {
|
|
impl_ = other.impl_;
|
|
return *this;
|
|
}
|
|
|
|
WeakPtr& operator=(WeakPtr&& other) noexcept {
|
|
impl_ = std::move(other.impl_);
|
|
return *this;
|
|
}
|
|
|
|
// Create/Assign from nullptr.
|
|
WeakPtr(std::nullptr_t) {} // NOLINT
|
|
|
|
WeakPtr& operator=(std::nullptr_t) {
|
|
impl_.reset();
|
|
return *this;
|
|
}
|
|
|
|
// Copy/Move constructors and assignment operators with upcast conversion.
|
|
template <typename U>
|
|
WeakPtr(const WeakPtr<U>& other) : impl_(other.as_std_weak_ptr()) {}
|
|
|
|
template <typename U>
|
|
WeakPtr(WeakPtr<U>&& other) noexcept
|
|
: impl_(std::move(other).as_std_weak_ptr()) {}
|
|
|
|
template <typename U>
|
|
WeakPtr& operator=(const WeakPtr<U>& other) {
|
|
impl_ = other.as_std_weak_ptr();
|
|
return *this;
|
|
}
|
|
|
|
template <typename U>
|
|
WeakPtr& operator=(WeakPtr<U>&& other) noexcept {
|
|
impl_ = std::move(other).as_std_weak_ptr();
|
|
return *this;
|
|
}
|
|
|
|
// Accessors.
|
|
T* get() const { return impl_.lock().get(); }
|
|
|
|
T& operator*() const {
|
|
T* const pointer = get();
|
|
OSP_DCHECK(pointer);
|
|
return *pointer;
|
|
}
|
|
|
|
T* operator->() const {
|
|
T* const pointer = get();
|
|
OSP_DCHECK(pointer);
|
|
return pointer;
|
|
}
|
|
|
|
// Allow conditionals to test validity, e.g. if (weak_ptr) {...}
|
|
explicit operator bool() const { return get() != nullptr; }
|
|
|
|
// Conversion to std::weak_ptr<T>. It is unsafe to convert in the other
|
|
// direction. See comments for private constructors, below.
|
|
const std::weak_ptr<T>& as_std_weak_ptr() const& { return impl_; }
|
|
std::weak_ptr<T> as_std_weak_ptr() && { return std::move(impl_); }
|
|
|
|
private:
|
|
friend class WeakPtrFactory<T>;
|
|
|
|
// Called by WeakPtrFactory<T> and the WeakPtr<T> upcast conversion
|
|
// constructors and assigners. These are purposely not being exposed publicly
|
|
// because that would allow a WeakPtr<T> to be valid/invalid by a different
|
|
// ownership/threading model than the intended one (see top-level comments).
|
|
template <typename U>
|
|
explicit WeakPtr(const std::weak_ptr<U>& other) : impl_(other) {}
|
|
|
|
template <typename U>
|
|
explicit WeakPtr(std::weak_ptr<U>&& other) noexcept
|
|
: impl_(std::move(other)) {}
|
|
|
|
std::weak_ptr<T> impl_;
|
|
};
|
|
|
|
// Allow callers to compare WeakPtrs against nullptr to test validity.
|
|
template <typename T>
|
|
bool operator!=(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
|
|
return weak_ptr.get() != nullptr;
|
|
}
|
|
template <typename T>
|
|
bool operator!=(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
|
|
return weak_ptr.get() != nullptr;
|
|
}
|
|
template <typename T>
|
|
bool operator==(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
|
|
return weak_ptr.get() == nullptr;
|
|
}
|
|
template <typename T>
|
|
bool operator==(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
|
|
return weak_ptr == nullptr;
|
|
}
|
|
|
|
template <typename T>
|
|
class WeakPtrFactory {
|
|
public:
|
|
explicit WeakPtrFactory(T* instance) { Reset(instance); }
|
|
WeakPtrFactory(WeakPtrFactory&& other) noexcept = default;
|
|
WeakPtrFactory& operator=(WeakPtrFactory&& other) noexcept = default;
|
|
|
|
// Thread-safe: WeakPtrs may be created on any thread/seuence. They may also
|
|
// be copied and moved on any thread/sequence. However, they MUST only be
|
|
// dereferenced on the same thread/sequence that calls the destructor or
|
|
// InvalidateWeakPtrs().
|
|
WeakPtr<T> GetWeakPtr() const {
|
|
return WeakPtr<T>(std::weak_ptr<T>(bookkeeper_));
|
|
}
|
|
|
|
// Destruction and Invalidation: These must be called on the same
|
|
// thread/sequence that dereferences any WeakPtrs to avoid use-after-free
|
|
// bugs.
|
|
~WeakPtrFactory() = default;
|
|
void InvalidateWeakPtrs() { Reset(bookkeeper_.get()); }
|
|
|
|
private:
|
|
WeakPtrFactory(const WeakPtrFactory& other) = delete;
|
|
WeakPtrFactory& operator=(const WeakPtrFactory& other) = delete;
|
|
|
|
void Reset(T* instance) {
|
|
// T is owned externally to WeakPtrFactory. Thus, provide a no-op Deleter.
|
|
bookkeeper_ = {instance, [](T* instance) {}};
|
|
}
|
|
|
|
// Manages the std::weak_ptr's referring to T. Does not own T.
|
|
std::shared_ptr<T> bookkeeper_;
|
|
};
|
|
|
|
} // namespace openscreen
|
|
|
|
#endif // UTIL_WEAK_PTR_H_
|