| #pragma once |
| |
| #include <c10/util/in_place.h> |
| |
| namespace c10 { |
| |
| // See example implementations in TensorBody.h and intrusive_ptr.h. |
| // Synopsis: |
| // |
| // repr_type -- type to use to store an owned T in ExclusivelyOwned. |
| // |
| // pointer_type -- pointer-esque type to return from |
| // ExclusivelyOwned's get() and operator*() methods. |
| // |
| // const_pointer_type -- similar to pointer_type, used for the const methods. |
| // |
| // static repr_type nullRepr() -- return a null instance of repr_type. |
| // |
| // template <class... Args> |
| // static repr_type createInPlace(Args&&... args) -- used by the in-place |
| // ExclusivelyOwned constructor. |
| // |
| // static repr_type moveToRepr(T&& x) -- move the given x into an |
| // instance of repr_type. used by the ExclusivelyOwned(T&&) |
| // constructor. |
| // |
| // static void destroyOwned(repr_type x) -- free memory for a |
| // known-exclusively-owned instance of x. Replaces calling repr_type's |
| // destructor. Being able to implement this more efficiently than |
| // repr_type's destructor is the main reason to use ExclusivelyOwned |
| // for a type. |
| // |
| // static T take(repr_type&) -- move out of the given repr_type into an owned T. |
| // |
| // static pointer_type getImpl(const repr_type&) -- return a pointer |
| // to the given repr_type. May take repr_type by value if that is more |
| // efficient. |
| template <typename T> |
| struct ExclusivelyOwnedTraits; |
| |
| /// ExclusivelyOwned is a smart-pointer-like wrapper around an |
| /// exclusively-owned instance of some type T that normally has |
| /// mandatory reference counting (currently Tensor or |
| /// c10::intrusive_ptr). If you have an isolated piece of code that |
| /// knows that it has sole ownership of an object of one of these |
| /// types (i.e., because you created it directly or using a factory |
| /// function) and that object will not escape from that isolated piece |
| /// of code, then moving the object into an ExclusivelyOwned will |
| /// avoid an atomic reference count decrement at destruction time. |
| /// |
| /// If you directly create the Tensor/intrusive_ptr in the first |
| /// place, you can use the in_place constructor of ExclusivelyOwned to |
| /// additionally avoid doing any stores to initialize the refcount & |
| /// weakcount. (Do note, however, that in this case you should |
| /// probably just use std::unique_ptr instead of intrusive_ptr if applicable.) |
| template <typename T> |
| class ExclusivelyOwned { |
| using EOT = ExclusivelyOwnedTraits<T>; |
| union { |
| char dummy_; |
| typename ExclusivelyOwnedTraits<T>::repr_type repr_; |
| }; |
| |
| public: |
| ExclusivelyOwned() : repr_(EOT::nullRepr()) {} |
| |
| explicit ExclusivelyOwned(T&& t) : repr_(EOT::moveToRepr(std::move(t))) {} |
| |
| template <class... Args> |
| explicit ExclusivelyOwned(in_place_t, Args&&... args) |
| : repr_(EOT::createInPlace(std::forward<Args>(args)...)) {} |
| |
| ExclusivelyOwned(const ExclusivelyOwned&) = delete; |
| |
| ExclusivelyOwned(ExclusivelyOwned&& rhs) noexcept |
| : repr_(std::move(rhs.repr_)) { |
| rhs.repr_ = EOT::nullRepr(); |
| } |
| |
| ExclusivelyOwned& operator=(const ExclusivelyOwned&) = delete; |
| |
| ExclusivelyOwned& operator=(ExclusivelyOwned&& rhs) noexcept { |
| EOT::destroyOwned(repr_); |
| repr_ = std::move(rhs.repr_); |
| rhs.repr_ = EOT::nullRepr(); |
| return *this; |
| } |
| |
| ExclusivelyOwned& operator=(T&& rhs) noexcept { |
| EOT::destroyOwned(repr_); |
| repr_ = EOT::moveToRepr(std::move(rhs)); |
| return *this; |
| } |
| |
| ~ExclusivelyOwned() { |
| EOT::destroyOwned(repr_); |
| // Don't bother to call the destructor of repr_, since we already |
| // did specialized destruction for the exclusively-owned case in |
| // destroyOwned! |
| } |
| |
| // We don't provide this because it would require us to be able to |
| // differentiate an owned-but-empty T from a lack of T. This is |
| // particularly problematic for Tensor, which wants to use an |
| // undefined Tensor as its null state. |
| explicit operator bool() const noexcept = delete; |
| |
| operator T() && { |
| return take(); |
| } |
| |
| // NOTE: the equivalent operation on MaybeOwned is a moving |
| // operator*. For ExclusivelyOwned, take() and operator*() may well |
| // have different return types (e.g., for intrusive_ptr, take() |
| // returns c10::intrusive_ptr<T> whereas operator* returns T&), so |
| // they are different functions. |
| T take() && { |
| return EOT::take(repr_); |
| } |
| |
| typename EOT::const_pointer_type operator->() const { |
| return get(); |
| } |
| |
| typename EOT::const_pointer_type get() const { |
| return EOT::getImpl(repr_); |
| } |
| |
| typename EOT::pointer_type operator->() { |
| return get(); |
| } |
| |
| typename EOT::pointer_type get() { |
| return EOT::getImpl(repr_); |
| } |
| |
| std::remove_pointer_t<typename EOT::const_pointer_type>& operator*() const { |
| return *get(); |
| } |
| |
| std::remove_pointer_t<typename EOT::pointer_type>& operator*() { |
| return *get(); |
| } |
| }; |
| |
| } // namespace c10 |